OrgrinRT's Learning Log AKA The pitch-in thread for helpful souls

Wow, huge thanks Chrisy15 for taking the time to explain this to me! It actually sheds enormous light on how things work, as well as the earlier post. I think I'm starting to get the hang of it, but there still are some things that I - for some reason - can't get through my head. If it's okay to you, I'll describe my struggles via the strip of code you and LeeS provided earlier. I'll do it in two parts - one for each function.

Code:
function C15_GPBorn(playerID, unitID, iX, iY)
    print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! UGP_FINN - C15_GPBorn function starts"
    local pPlayer = Players [playerID]
    if pPlayer:GetCivilizationType() == civilizationID then
        local pUnit = pPlayer:GetUnitByID(unitID)
        if pUnit ~= nil then
            if tGreatPeopleClassesToProcess[pUnit:GetUnitClassType()] and not pUnit:IsHasPromotion(iPromotionUnitBorn) then
                local sSelectedName = GetNameForGreatPerson(pUnit)
                pUnit:SetName(sSelectedName)
                Events.UnitNameChanged( pUnit:GetOwner(), pUnit:GetID() );
                pUnit:SetHasPromotion(iPromotionUnitBorn)
                    if iPromotionUnitBorn ~= nil then
                    print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! UGP_FINN - Promotion has value, runs the function to the end!")
            end
        end
    end
    print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! UGP_FINN - C15_GPBorn function ran")
end
if JFD_IsCivilisationActive(civilizationID)
    GameEvents.UnitSetXY.Add(C15_GPBorn)
        print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! UGP_FINN - UnitSetXY event detected, adding values to C15_GPBorn - function should run")
end


So I put some print messages there to see in firetuner where we go. For some reason my firetuner just crashes on game start so I've never been actually able to see these printed, but at least none of the logs actually show them. Which leads me to believe that either A) I'd need a different command than print() to show things in any of the logs, or B) the script just doesn't run.

This would be, from my understanding, the first function to run. The other one, GetNameForGreatPerson would run after this one, so I'll get to it after this.

I can understand the logic of it all the way to this part:

Code:
                Events.UnitNameChanged( pUnit:GetOwner(), pUnit:GetID() );

Does this feed a new event to the system? Because I'm not really sure on how to understand this bit. Are the events in general just sort of functions in that the stuff inside the brackets are the values anything that uses this event would then feed in place of whatever variables they have? For example if I did a function like this

Code:
function Example1(pUnit:GetOwner(), pUnit:GetID())
    local UnitOwner = pUnit:GetOwner()
    local civilizationID = ["CIVILIZATION_FINNS"]
    if UnitOwner = civilizationID then
        local TheCoolFinnsPromotion = GameInfoTypes["PROMOTION_THECOOLFINNS"]
        pUnit:SetHasPromotion(TheCoolFinnsPromotion)
        else
        end
    end
    local TheUnit = pUnit:GetID()
    if TheUnit = GameInfoTypes.UNIT_WARRIOR then
        local TheCoolWarriorsPromotion = GameInfoTypes["PROMOTION_THECOOLWARRIORS"]
        pUnit:SetHasPromotion(TheCoolWarriorsPromotion)
        else
        end
    end
end

GameEvents.UnitNameChanged.Add(Example1)

Would it then, in the event of a UnitNameChanged, check if the unit was a Finnish unit, and if so, give it the cool promotion and also check if it is a warrior and if it is, give it the other cool promotion? I know this function is pretty pointless but I just quickly did something to use the UnitNameChanged values it sets in the code LeeS gave.

Am I understanding it correctly? All I need to add the values from an event is to use GameEvents, and the .Add part and then define the function it feeds the values to in the brackets?

Okay, the other function:


Code:
function GetNameForGreatPerson(pUnit)
    print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! UGP_FINN - GetNameForGreatPerson function ran" 
    if pUnit ~= nil then
        local sUnitType = GameInfo.Units[pUnit:GetUnitType()].Type
        local tCorrectUnitNames = {}
        for row in GameInfo.Unit_UniqueNames("UnitType='" .. sUnitType .. "'") do
            table.insert(tCorrectUnitNames, row.UniqueName)
        end
        if #tCorrectUnitNames > 0 then
            local sSelection = tCorrectUnitNames[1]
            if #tCorrectUnitNames > 1 then
                sSelection = tCorrectUnitNames[JFD_GetRandom(1, #tCorrectUnitNames)]
            end
            return sSelection
        end
    end
    return "Tuntematon Sotilas"
    print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! UGP_FINN - GetNameForGreatPerson function ran"
end


These prints don't come up in the logs either, and as said, my firetuner dislikes me, so can't really tell why or if they're supposed to at all. But:

The table here is confusing me.

Code:
        local sUnitType = GameInfo.Units[pUnit:GetUnitType()].Type
        local tCorrectUnitNames = {}
        for row in GameInfo.Unit_UniqueNames("UnitType='" .. sUnitType .. "'") do
            table.insert(tCorrectUnitNames, row.UniqueName)
        end


It's a little heavy in my eyes. So correct me if I'm wrong:

With the local define it creates the table, and then fills it with the table.insert line with the UniqueName of each row. The rows it seeks are in table Unit_UniqueNames, and it only goes through those that have UnitType= GameInfo.Units[pUnit:GetUnitType()].Type in it. I really don't understand what the whole UnitType= GameInfo.Units[pUnit:GetUnitType()].Type part really means. Would the UnitType= mean that it checks the UnitType column (and would this be a standard way to type it in?), and then the GameInfo.Units[pUnit.GetUnitType()].Type part just defines to look for the unittypes of pUnit. pUnit was not defined anywhere in the code, so I'm guessing it's a standard thing, but I really don't know what it means. Is it just a general tag for units that are in use for the player's civilization? The prefixes like "p" or "i" etc. still confuse me a little bit as well.

If I got the above part right, I'm guessing that tCorrectUnitNames then actually fills the table with every unittype that the civilization can use. Wouldn't this also add the names that aren't civ specific? So I would have to alter this bit of code to include the CivilizationType somehow. How would I go about it? I'm guessing the best way would be to add it in the

Code:
        for row in GameInfo.Unit_UniqueNames("UnitType='" .. sUnitType .. "'") do

part. But I can't think of the commands to do that, so what I had just freestyled there earlier, made a whole new lines for that like this:

Code:
        local sUnitType = GameInfo.Units[pUnit:GetUnitType()].Type
        local tCorrectUnitNames = {}
        for row in GameInfo.Unit_UniqueNames("UnitType='" .. sUnitType .. "'") do
        for row in GameInfo.Unit_UniqueNames("CivilizationType='" .. sUnitType .. "'") do
            table.insert(tCorrectUnitNames, row.UniqueName)
        end

Does this make sense? In my head it now does both of the for loops before going to the table.insert part. But it doesn't exactly require both, I think it only kind of goes for either. So I'd need a way to sum them as a single query. I'm scratching my head here...

I'll stop here for now, otherwise there'll be too much stuff here and nobody cba to help me :D


EDIT: I'm guessing just a simple "and" can't be enough? If I did:

Code:
        local sUnitType = GameInfo.Units[pUnit:GetUnitType()].Type
        local tCorrectUnitNames = {}
        for row in GameInfo.Unit_UniqueNames("UnitType='" and "CivilizationType="CIVILIZATION_FINNS" .. sUnitType .. "'") do
            table.insert(tCorrectUnitNames, row.UniqueName)
        end


EDIT2:

Actually, I think I have a hunch now on how it should be done.

Code:
        local sUnitType = GameInfo.Units[pUnit:GetUnitType()].Type
        local tCorrectUnitNames = {}
        for row in GameInfo.Unit_UniqueNames("UnitType='" .. sUnitType .. "'") and ("CivilizationType="CIVILIZATION_FINNS") do
            table.insert(tCorrectUnitNames, row.UniqueName)
        end

I'm guessing it can't be that easy :D but just messing around, I guess this is the best way to try and understand how to create this stuff.
 
Last edited:
if JFD_IsCivilisationActive(civilizationID)

This'll be one issue; Syntax error. 'if' must be followed by 'then'.

GameEvents.UnitNameChanged.Add(Example1)

Is this a GameEvent? If you're hoping you can make your own GameEvents, you're gonna be out of luck :p

and ftr:

Code:
function Example1(pUnit:GetOwner(), pUnit:GetID())

That won't work. What you'd do here is have

Code:
function Example1(unitOwnerID, unitID)
-- and then you'd call

Example1(pUnit:GetOwner(), pUnit:GetID())

With the local define it creates the table, and then fills it with the table.insert line with the UniqueName of each row. The rows it seeks are in table Unit_UniqueNames, and it only goes through those that have UnitType= GameInfo.Units[pUnit:GetUnitType()].Type in it. I really don't understand what the whole UnitType= GameInfo.Units[pUnit:GetUnitType()].Type part really means. Would the UnitType= mean that it checks the UnitType column (and would this be a standard way to type it in?), and then the GameInfo.Units[pUnit.GetUnitType()].Type part just defines to look for the unittypes of pUnit.

Pretty much, yeah. pUnit:GetUnitType() returns the ID of the row that has pUnit's Type in. You then get the Type (aka UNIT_WARRIOR) through the .Type.

pUnit was not defined anywhere in the code, so I'm guessing it's a standard thing, but I really don't know what it means. Is it just a general tag for units that are in use for the player's civilization?

No; pUnit's defined in the function() line as whatever's been passed into the function. When you write something inside those brackets when defining the function, you're creating local variables that the function then uses.

The prefixes like "p" or "i" etc. still confuse me a little bit as well.

These are just good practice for telling the reader what's stored in the function. "p" stands for pointer (aka "row"), "i" == Integer, "b" == Boolean, "t" == Table. They aren't necessary, and are merely personal preference.

Code:
local sUnitType = GameInfo.Units[pUnit:GetUnitType()].Type
local tCorrectUnitNames = {}
for row in GameInfo.Unit_UniqueNames("UnitType='" .. sUnitType .. "'") do
for row in GameInfo.Unit_UniqueNames("CivilizationType='" .. sUnitType .. "'") do
table.insert(tCorrectUnitNames, row.UniqueName)
end
Does this make sense? In my head it now does both of the for loops before going to the table.insert part. But it doesn't exactly require both, I think it only kind of goes for either. So I'd need a way to sum them as a single query. I'm scratching my head here...

That's not gonna work, given that you'd be searching the CivilizationType column for UNIT_WARRIOR. You can use SQL statements in Lua; not many people seem to have realised this though, so don't expect much documentation:

Code:
for row in DB.Query("SELECT UnitType, CivilizationType FROM Unit_UniqueNames WHERE UnitType = '".sUnitType.."' AND CivilizationType = '..GameInfo.Civilizations[Players[pUnit:GetOwner()]:GetCivilizationType()].Type..'") do

Honestly dunno about the \ business tbh :p

And yes, I know I've been very naughty with that civ-getting statement; you're not gonna have any idea what it means, but it's the same as doing:

Code:
local playerID = pUnit:GetOwner()
local pPlayer = Players[playerID]
local civilizationID = pPlayer:GetCivilizationType()
local sCivType = GameInfo.Civilizations[civilizationID].Type

for row in DB.Query("SELECT UnitType, CivilizationType FROM Unit_UniqueNames WHERE UnitType = '".sUnitType.."' AND CivilizationType = '..sCivType..'") do

...Presuming I've done it right, of course... :p
 
Urgh, knew I'd forget something

Are the events in general just sort of functions in that the stuff inside the brackets are the values anything that uses this event would then feed in place of whatever variables they have?

GameEvents are the equivalent of you calling a function. So:

Code:
function example(playerID)
return playerID
end

print(example(pPlayer:GetID())) -- Would print the ID in the log

GameEvents.SomethingThatWantsAPlayerIDForSomeReason.Add(example) -- This would return the playerID to the Game Engine whenever the engine calls this event.

This is a bad example.

If you look in WH's excel sheet, you'll see that there are 2 common types of GameEvent: "All" and "Hook" (There's also "Any" and "Acc", but ngl I don't know/can't remember what they do)

Hooks are things like PlayerDoTurn. The engine fires them whenever something happens (PlayerDoTurn fires at the start of each player's turn, CityCaptureComplete fires after city capture (y'know, after all the buildings are destroyed... *grumble*) etc.)

Alls are GameEvents that you actually return stuff into - usually a boolean. Example:

Code:
function CanTrainIfIsRich(playerID, unitID)
local pPlayer = Players[playerID] -- if it hasn't been explained, Players is a table with all the game's players in it. Barbs are always Players[63]
if pPlayer:GetGold() > 500 then -- If the player has more than 500 gold in the treasury, then he can build the unit in one of his cities
return true
else -- Otherwise...
return false
end
end

GameEvents.PlayerCanTrain.Add(CanTrainIfRich)
 
Is this a GameEvent? If you're hoping you can make your own GameEvents, you're gonna be out of luck :p

Uhh.. Not sure at all :lol: This is just the code LeeS posted as a sample, so I haven't written it, so I don't really know.

I did run a Agent Ransack and it points to CivilizationV.exe as a result, so I'm definitely leaning towards it being an actual event.

(Not gonna paste a bunch of quotes, it's gonna get long and messy if I do, but: )

Yeah, I think I'm understanding the game events now. Actually, I'm gonna make it a habit to run the search in every occasion I need to know if there's an event for something. Unless the events are in the W's method tables? Though the searches are pretty fast with ransack, so I think it's inconsequential which I choose to use.

AND why didn't I know you can use SQL stuff in lua :confused: I've got somewhat accustomed to those earlier when meddling with the tables, so it'd have made it a bunch easier for me to figure this whole thing out if I knew this :D So what I'm getting here, is that you can call sql stuff with the db.query? Though, still probably wouldn't have ended up with the bit you gave, which seems to actually take everything needed into consideration.

If firetuner worked this would be a bit easier I think, but I'll start the trial-and-error process with starting and restarting games after some changes/potential fixes. Hopefully I'll get this done within a week so I can actually start continuing with other civs.


EDIT: Nevermind..... stupid me.

the two full stops (..) are what Lua uses to append strings, and so the code



EDIT2:

....yeah, well, turns out the script had a big bunch of syntax errors. A lot of missing ending brackets... I did a bad job writing stuff. Anyway, let's see if I can make the thing run for the first time now.
 
Last edited:
Obviously I'm doing something wrong here. I tried to integrate Machiavelli's SerialEventUnitCreatedGood as per LeeS's suggestion, but I'm guessing something I've changed now doesn't fare so well with it.

Here's what I have:
Spoiler :

Code:
function GetNameForGreatPerson(pUnit)
    if pUnit ~= nil then
        local sUnitType = GameInfo.Units[pUnit:GetUnitType()].Type
        local playerID = pUnit:GetOwner()
        local pPlayer = Players[playerID]
        local sCivType = GameInfo.Civilizations[civilizationID].Type
        local tCorrectUnitNames = {}
            for row in DB.Query("SELECT UnitType, CivilizationType FROM Unit_UniqueNames WHERE UnitType = '..sUnitType..' AND CivilizationType = '..sCivType..'") do
                table.insert(tCorrectUnitNames, row.UniqueName)
            end
        if #tCorrectUnitNames > 0 and not > 1 then
            sSelection = tCorrectUnitNames[1]
            if #tCorrectUnitNames > 1 then
                sSelection = tCorrectUnitNames[JFD_GetRandom(1, #tCorrectUnitNames)]
            end
            return sSelection
        end
    end
    return "Tuntematon Sotilas"
end

function C15_GPBorn(playerID, unitID, hexVec, unitType, cultureType, civID, primaryColor, secondaryColor, unitFlagIndex, fogState, selected, military, notInvisible)
    local pPlayer = Players [playerID]
    if pPlayer:GetCivilizationType() == civilizationID then
        local pUnit = pPlayer:GetUnitByID(unitID)
        if pUnit ~= nil then
            if iPromotionUnitBorn ~= nil then
                if tGreatPeopleClassesToProcess[pUnit:GetUnitClassType()] and not pUnit:IsHasPromotion(iPromotionUnitBorn) then
                    local sSelectedName = GetNameForGreatPerson(pUnit)
                    pUnit:SetName(sSelectedName)
                    --Events.UnitNameChanged( pUnit:GetOwner(), pUnit:GetID() );
                    --pUnit:SetHasPromotion(iPromotionUnitBorn)
                    Events.UnitNameChanged( pUnit:GetOwner(), pUnit:GetID() );
                end
            end
        end
    end
end

if JFD_IsCivilisationActive(civilizationID) then
    LuaEvents.SerialEventUnitCreatedGood.Add(C15_GPBorn)
end


The SerialEventUnitCreatedGood goes as follows:

Spoiler :
Code:
-- Unit_CreatedFunctions.lua
-- Author: Machiavelli
-- DateCreated: 6/1/2012 9:02:22 AM
-- SerialEvenUnitCreatedGood by Machiavelli, this file from LeeS's Great People Names mod!
--------------------------------------------------------------
function CallSerialEventUnitCreatedGood(playerID, unitID, hexVec, unitType, cultureType, civID, primaryColor, secondaryColor, unitFlagIndex, fogState, selected, military, notInvisible)
    if(Players[playerID] == nil or
        Players[playerID]:GetUnitByID(unitID) == nil or
        Players[playerID]:GetUnitByID(unitID):IsDead() or
        Players[playerID]:GetUnitByID(unitID):IsHasPromotion(GameInfoTypes["PROMOTION_CREATED"])) then
        return;
    end

    -- Always mark the unit with the created promotion
    Players[playerID]:GetUnitByID(unitID):SetHasPromotion(GameInfoTypes["PROMOTION_CREATED"], true);

    -- Call the good event
    LuaEvents.SerialEventUnitCreatedGood(playerID, unitID, hexVec, unitType, cultureType, civID, primaryColor, secondaryColor, unitFlagIndex, fogState, selected, military, notInvisible);
end

--------------
-- Initialization check.  Ensures this code isn't loaded twice
--------------
local retVal = {};
LuaEvents.SerialEventUnitCreatedGood_IsInitialized(retVal);

-- If retVal isn't changed, no other mod has initialized this code.
if (retVal.isInitialized == nil) then
    LuaEvents.SerialEventUnitCreatedGood_IsInitialized.Add(function (retVal) retVal.isInitialized = true; end);
    -- Initialize the code
    Events.SerialEventUnitCreated.Add(CallSerialEventUnitCreatedGood);
end


The way I'm reading this, Machiavelli's LuaEvent basically changes the SerialEventUnitCreated so that it checks if

1)the player exists
2)the unit in question exists
3)the unit isn't dead
4)the unit doesn't already have the promotion

BUT all of the above hold true, it continues to give the unit the promotion and creates the LuaEvent.

I'm not seeing how I can actually use this bit to help me recognize actually created units. If I add the LuaEvent into the script, then it automatically assigns the promotion to every unit that is created if the conditions are met, right? So every unit that is created, gets the promotion.

Now when I want to do the name change, it has to check if the unit is of the class that is defined to be in the table that contains the UnitClasses that need their name changed. Then it has to check either if the unit A) has the promotion or B) doesn't have the promotion. Now here's my real struggle, because I can't figure out which way it should be. The dummy promotion is created for a reason, but I can't really figure out just that. The promotion is given to every spawned unit. So the promotion exists in every unit that is currently in the game that meet the conditions.

And the promotion is given before I can do anything regarding the name change. So any unit that is going through the process of name change, already has to have that promotion.

Am I right here? So instead of "and not" it should be "and"? But if the promotion is automatically given to every unit that is not dead and exists in the first place, isn't it rather pointless? Or is this just to shave off the dead units and such?

Anyway, I can't get the script running in the first place. I'm probably not doing the triggering part right, so nothing fires it. Any suggestions? :confused:


EDIT: Haha, I think I answered my own question there. So the better event for unit creation is there to only trigger the event if the units exist and are not dead etc. Now I get that.

But I'm definitely still doing something wrong here, since the script refuses to do anything.
 
Last edited:
Code:
table.insert(LuaTableName, DataToBeAddedAsNewValueInLuaTableName)
Adds a new Value (or "v") to an existing lua table, and not an xml table

  • LuaTableName = the name of the previously defined lua table
  • DataToBeAddedAsNewValueInLuaTableName = the data to be added as a new "v" in the lua table. This new data can be an integer, a boolean (true/false), a text string such as "Cheeseburgers", or it can be itself an lua table.

Lua auto-increments the key numbers ("k") of LuaTableName to add a new key one number higher than the last integer already within the table. But this only works properly when the table's keys ("k"s) are in the form of a numerical array. A numerical array (as used in lua tables) is a series of positive whole numbers starting at "1" and with no skipped values. When an lua table is constructed to conform to this definition of an array, we speak of the lua table as being an array. Otherwise we speak of it as being a hash. The "ipairs" iterator through an lua table only runs through that portion of the specified lua table that conforms to an array. If an lua table contains a portion that is an array, and a portion that is not, "ipairs" runs through only the array portion.
Code:
table ={ [1] = "dog", [2] = "cat", [3]= "mouse", [4]="rat", Elephant = "broken house"}
for k,v in ipairs(table) do print(k,v) end
results in
Code:
1	dog
2	cat
3	mouse
4	rat
Whereas
Code:
table ={ [1] = "dog", [2] = "cat", [3]= "mouse", [4]="rat", Elephant = "broken house"}
for k,v in pairs(table) do print(k,v) end
results in
Code:
Elephant	broken house
1	dog
2	cat
3	mouse
4	rat
The "#" operator for getting a table length is also affected by whether or not the table is an array, and whether or not it is an array throughout it's entire length.
Code:
table ={ [1] = "dog", [2] = "cat", [3]= "mouse", [4]="rat", Elephant = "broken house"}
print("The length of the table is " .. #table)
results in
Code:
The length of the table is 4

The "\" symbol in SQL as it is being used in the example means that literal matching to whatever follows is required. It is not necessary in the specified example, though. The line could just as easily have been written as:
Code:
for row in GameInfo.Units("CombatClass='" .. sCombatType .. "'") do
 
This sort of construction
Code:
local Cheeseburgers = GetTastiestCheeseburgerPlayerCanHave(pPlayer)
Tells lua to execute a function called GetTastiestCheeseburgerPlayerCanHave and stick whatever value this function returns into variable Cheeseburgers. This requires the function specified to return some kind of value as a result of being executed, or variable Cheeseburgers will be interpretted by lua as having a value of nil. This is not always a bad thing, but if you allow function GetTastiestCheeseburgerPlayerCanHave to return garbage info or "nil" when appropriuate, then you need to make your code account for the possibility. Generally we would do this by this method:
Code:
local Cheeseburgers = GetTastiestCheeseburgerPlayerCanHave(pPlayer)
if Cheeseburgers ~= nil then
     --do some stuff here
end
 
Last edited:
Using that as a basis, I can't really find the error in the bit I've pieced together;

Code:
        local sUnitType = GameInfo.Units[pUnit:GetUnitType()].Type
        local playerID = pUnit:GetOwner()
        local pPlayer = Players[playerID]
            local tCorrectUnitNames = {}
                for row in DB.Query("SELECT UnitType, CivilizationType FROM Unit_UniqueNames WHERE UnitType = \'" .. sUnitType .. "\' AND CivilizationType = 'CIVILIZATION_FINNS'") do
                    table.insert(tCorrectUnitNames, row.UniqueName)
                end

To me it seems it's correctly referring to the tCorrectUnitNames when using table.insert, and I ran the SQL command in SQLite a couple of times and with different unittypes to see if that's right, and it definitely is. So either the problem is with the sUnitType reference there, or something else. I'm leaning towards something else, because I can't get any of my print calls to actually print anything :sad:
 
Machi's event only fires when the unit is created; you shouldn't have to do any checking yourself. What Machi's done is create an event that fires whenever SerialEventUnitCreated is triggered, and then checks to see whether this unit has actually been created or if the event's firing after embarkation or upgrade using the promotion logic.

This

Code:
        if #tCorrectUnitNames > 0 and not > 1 then
            sSelection = tCorrectUnitNames[1]
            if #tCorrectUnitNames > 1 then
                sSelection = tCorrectUnitNames[JFD_GetRandom(1, #tCorrectUnitNames)]
            end
            return sSelection
        end

can be written more efficiently as

Code:
if #tCorrectUnitNames > 0 then
	if #tCorrectUnitNames == 1 then
		return tCorrectUnitNames[1]
	else
		return tCorrectUnitNames[JFD_GetRandom(1, #tCorrectUnitNames)]
	end
end

As for my function:

Code:
function C15_GPBorn(playerID, unitID)
    local pPlayer = Players[playerID]
    if pPlayer:GetCivilizationType() == civilizationID then
        local pUnit = pPlayer:GetUnitByID(unitID)
        if pUnit then
			if tGreatPeopleClassesToProcess[pUnit:GetUnitClassType()] then
				local sSelectedName = GetNameForGreatPerson(pUnit)
				if sSelectedName then -- If the civ has no entries, then this will be nil; we must check for this
					pUnit:SetName(sSelectedName)
					Events.UnitNameChanged(playerID, unitID)
				end
			end
		end
	end
end

WH's GameEvents spread should've been in the same thread as the methods; if you missed it, I'll add it here.
 

Attachments

  • GameEvents.xlsx
    12.9 KB · Views: 63
To me it seems it's correctly referring to the tCorrectUnitNames when using table.insert, and I ran the SQL command in SQLite a couple of times and with different unittypes to see if that's right, and it definitely is. So either the problem is with the sUnitType reference there, or something else. I'm leaning towards something else, because I can't get any of my print calls to actually print anything :sad:

Add print statements wherever something might be going wrong; usually after boolean tests. This will help you tell how far the code's getting before stopping working.

Are you getting any runtime errors in your Lua log?
 
Add print statements wherever something might be going wrong; usually after boolean tests. This will help you tell how far the code's getting before stopping working.

Are you getting any runtime errors in your Lua log?

Yeah, I have a bunch of prints in there, I just removed them in the quote to make it less messy. None of them print anything. I have one associated with the LuaEvent.SerialEventUnitCreatedGood as well, and that's not printing anything either. But I am creating units, though now that I think of it, I'm doing it via the IGE - could the units created with it bypass the original games' SerialEventUnitCreated?

Anyway, there's no mention of this script in any form in the lua logs. Also neither the database log or the xml log give any errors regarding to this (just for the chance that I've done goofed with the actual namelists). It's like the code isn't there in the first place. So my thinking is, it's not starting. So I'm missing something I have to do to make it run. Like with the sql and xml files, I had to add the OnModActivated sequence there, maybe luas need something similiar? I'm definitely missing something because it's like it doesn't exist.
 
Anyway, there's no mention of this script in any form in the lua logs. Also neither the database log or the xml log give any errors regarding to this (just for the chance that I've done goofed with the actual namelists). It's like the code isn't there in the first place. So my thinking is, it's not starting. So I'm missing something I have to do to make it run. Like with the sql and xml files, I had to add the OnModActivated sequence there, maybe luas need something similiar? I'm definitely missing something because it's like it doesn't exist.

Yup, Lua files need setup too:

Under the "Content" tab in Properties:
  • First select the file in the bottom dropdown
  • Choose InGameUIAddin (Yes, I know you're not actually making a UI change; just go with it)
  • Fill in the Description with something useful
Now the file should load into the game. Dunno what's wrong with your tuner tho.
 
  1. In Post #14 I did forget the one "then" that Crisy pointed out. I have added the needed "then" to the code shown there.
  2. Don't see why it is necessary to attempt re-writing this part
    Code:
    if #tCorrectUnitNames > 0 then
    	local sSelection = tCorrectUnitNames[1]
    	if #tCorrectUnitNames > 1 then
    		sSelection = tCorrectUnitNames[JFD_GetRandom(1, #tCorrectUnitNames)]
    	end
    	return sSelection
    end
  3. The problem you had with using Machiavelli's system would seem to be that you nowhere are using
    Code:
    LuaEvents.SerialEventUnitCreatedGood.Add(SomeFunctionName)
  4. Your other problems are stemming from a fundamental misunderstanding of everything that is going on in CIV-Linked Great Generals.
  5. This is the standard base-game definition of xml table <Unit_UniqueNames>
    Code:
    <Table name="Unit_UniqueNames">
    	<Column name="UnitType" type="text" reference="Units(Type)"/>
    	<Column name="UniqueName" type="text" notnull="true" reference="Language_en_US(Tag)"/>
    	<Column name="GreatWorkType" type="text" reference="GreatWorks(type)"/>
    </Table>
    1. Nowhere is there a column valid for that table called CivilizationType.
    2. I am adding the column-name to the table <Unit_UniqueNames> via this statement in an SQL file:
      Code:
      ALTER TABLE Unit_UniqueNames ADD CivilizationType text REFERENCES Civilizations(Type) default null;
    3. So unless you also add such a column to the table in an SQL file containing no other commands, or you force your mod to require CIV-Linked Great Generals to be present and enabled by a player, your lua is going to fail every time you attempt to access a column called CivilizationType within xml-table Unit_UniqueNames.
    4. You will note that I wrote the code shown in Post #14 to conform to the base-game's definition of xml-table Unit_UniqueNames.
 
Yup, Lua files need setup too:

Under the "Content" tab in Properties:
  • First select the file in the bottom dropdown
  • Choose InGameUIAddin (Yes, I know you're not actually making a UI change; just go with it)
  • Fill in the Description with something useful
Now the file should load into the game. Dunno what's wrong with your tuner tho.

...yeah. I knew it. Something this simple, and I was completely oblivious to its existence.

It's a very distinct feeling, when you realize you've needlessly wasted a lot of others' time. The whole problem's actually just been about me not knowing such a fundamental thing.

Ugh.

Anyway, it's now working as it should. Turns out, it's probably been working this whole time... Whaddaya know? Most of the head-scratching today has been useless. Oh well. :rolleyes:

Onwards, to future dissappointments!

  1. In Post #14 I did forget the one "then" that Crisy pointed out. I have added the needed "then" to the code shown there.
  2. Don't see why it is necessary to attempt re-writing this part
    Code:
    if #tCorrectUnitNames > 0 then
        local sSelection = tCorrectUnitNames[1]
        if #tCorrectUnitNames > 1 then
            sSelection = tCorrectUnitNames[JFD_GetRandom(1, #tCorrectUnitNames)]
        end
        return sSelection
    end
  3. The problem you had with using Machiavelli's system would seem to be that you nowhere are using
    Code:
    LuaEvents.SerialEventUnitCreatedGood.Add(SomeFunctionName)
  4. Your other problems are stemming from a fundamental misunderstanding of everything that is going on in CIV-Linked Great Generals.
  5. This is the standard base-game definition of xml table <Unit_UniqueNames>
    Code:
    <Table name="Unit_UniqueNames">
        <Column name="UnitType" type="text" reference="Units(Type)"/>
        <Column name="UniqueName" type="text" notnull="true" reference="Language_en_US(Tag)"/>
        <Column name="GreatWorkType" type="text" reference="GreatWorks(type)"/>
    </Table>
    1. Nowhere is there a column valid for that table called CivilizationType.
    2. I am adding the column-name to the table <Unit_UniqueNames> via this statement in an SQL file:
      Code:
      ALTER TABLE Unit_UniqueNames ADD CivilizationType text REFERENCES Civilizations(Type) default null;
    3. So unless you also add such a column to the table in an SQL file containing no other commands, or you force your mod to require CIV-Linked Great Generals to be present and enabled by a player, your lua is going to fail every time you attempt to access a column called CivilizationType within xml-table Unit_UniqueNames.
    4. You will note that I wrote the code shown in Post #14 to conform to the base-game's definition of xml-table Unit_UniqueNames.

1. Yeah, that was pointed out and I did fix it with the suggestion.
2. The script wasn't working, so I just went over and beyond trying to change anything that might have caused it. Thought the problem was already discovered... and I feel ashamed..
3. I do have that there, in the end, if you check it. At least it's in the code I have in modbuddy, maybe I accidentally cut my copypaste a few lines too soon.. oops :rolleyes:
4. Yeah, I do have a very fundamental lack of knowledge about anything, to be honest
5. Yeah, I've created that column there. I have been meddling with the database and the tables quite a lot earlier so at least there's something I know how to do :D
 
I wouldn't say you'd wasted anyone's time today tbh; you've learnt a good bit today obviously. You're honestly doing better than some people who have been at it for months!
 
Just make sure the SQL line I quoted adding the column is in a file all by itself. That way, if you use Great General names at the same time as your mod, a repeat of the line within an SQL will not take out other code when the game generates a fatal-level error for attempting to define a column that already exists.

So if Great General Names loads into the game 1st, the column is already defined, your SQL file generates an error, but nothing else is adversely affected.
If your mod loads before Great General Names, however, my SQL line will fail, but nothing else will be adversely affected.
In both conditions the needed alteration to the table will be made.

If Great General Names loads after your mod, though, it will wipe anything you add to the Unit_UniqueNames table for Great Generals and Great Admirals but will not affect any other types of units.
 
Hmm. Okay so that's going to be tricky, seeing as I want this to be as compatible as possible.

The way I've set it up right now, is that the new column adding bit is in its own file. It runs, obviously, before the file that fills in the Unit_UniqueNames with the CivilizationType.

So my thinking is this:

1. If I add reference to your mod, your mod would then always be loaded first, which would keep your mod always working as it should
2. If the user doesn't have your mod, this mod would add the column and make use of the rest as usual
3. If the user does indeed have your mod, your mod would add the column, and:
---1. My seperate file that would create the column would give an error - this would be of no consequence, since it's already there
---2. The changes your mod does, would remain there as it was loaded first
---3. The changes my mod does, are specific to the civs that I add the "immersion" to, so it will then INSERT INTO Unit_UniqueNames, but only the custom civs that I would include

So the priority number one is to always make your mod run first and fully working, and if one fails, it should of course be mine. But the way I see it, neither would actually fail, only the column-creating file that is in my mod. Right?

It would be great if there was a sql trigger that would work like the table-creating one (CREATE TABLE IF NOT EXISTS) but for entries. I.e, INSERT INTO IF NOT EXISTS. I really don't know much about these things, so maybe there is a way to do it like so?

I think I could use the UPDATE one, but that wouldn't really work unless I inserted the names first without CivilizationType. But actually, would it be possible to do it so that it updates the fields that have _FINN_ in the UniqueName field? I mean, that'd solve all the problems. So, my file would only update the table, and only the files that have _FINN_ in the txt_key reference.

I'm thinking maybe something like this:

Code:
UPDATE Unit_Uniquenames
SET CivilizationType = 'CIVILIZATION_FINNS'
WHERE UniqueName ='*_FINN_*'

I'll try and see if something like that might work. Not sure how I would make use of that _FINN_ bit, I'm pretty sure it doesn't work the way I have it there. But I'm fairly sure there's a way to do what I want to do, I just don't know the correct way to type it. I'll see to it.

But would this solve all the problems regarding both mods being enabled at the same time?


EDIT:

Well, eventhough that *_FINN_* doesn't work, as I now tested, I could definitely add every single line seperately there. That would, though, be non-modular in the sense that if I added more, I would have to add them also to this update file. There has to be a way to use the _FINN_ part, because each of the names I add have that in common.


EDIT2:

Isn't this actually a non-issue, now that I think of it?

If my mod runs after your mod, my mod will just simply insert new stuff into the table. Not change anything that's already there. I only provide new names with this modmod, so really there's no data to be lost, right?

So actually I'm just overthinking this; it works as is, as long as I don't touch any of those vanilla names or the ones you may provide with your mod etc. ?


EDIT3:

Yeah it's definitely a non-issue.

As long as I'm just adding new stuff, nothing's gonna go wrong. Only thing that will fail if both are activated, is the sql file I have that tries to add the column, but that's also a non-issue since the column still exists, which is the point anyway.
 
Last edited:
Yup, you were overthinking. Happens to all of us at one time or another.

Ah, knew it.

I'm still very new to all this, so I'm a big mess as it is. I hope I'm not being too big of a trouble :undecide: BUT huge thanks for the input, I couldn't have got this working without!
 
Top Bottom