Any gotchas with Lua scripts?

Irkalla

ENTP POWWWEEEEEER
Joined
Sep 25, 2012
Messages
1,009
Location
Way down around Vicksburg
Can't get this Lua script to run. The desired effect does not come to pass, and I get no Console errors. This leads me to believe the script isn't running.

I have the script added to VFS. Not sure if I'm supposed to do anything else. I figure I'm supposed to tell something to include it somehow, but I'm not sure what to do.
 
VFS = false
and then in the Content tab add an InGameUIAddin for the lua file (if the lua file doesn't show in the bottom drop-down, either type the name in by hand - a BAD idea unless you know all the rules to follow - better is to change the Type drop-down to "Map Script", then select the lua file, then change theh Type drop-down to "InGameUIAddin")
 
This leads me to believe the script isn't running.

Put something like

Code:
print("This is the 'Buildings - Lighthouse Near Sea' mod script.")

as the first line of your lua file, the FireTuner console and the lua.log file will then tell you what is being loaded (and also in which order as that can be important when debugging - while you can assume files will always load in the same order within a mod, unless you specifically set associations, never assume two mods load the same way between games)
 
Put something like

Code:
print("This is the 'Buildings - Lighthouse Near Sea' mod script.")

as the first line of your lua file, the FireTuner console and the lua.log file will then tell you what is being loaded (and also in which order as that can be important when debugging - while you can assume files will always load in the same order within a mod, unless you specifically set associations, never assume two mods load the same way between games)

I've got that, and I've also got prints for most every part of every function.

Code:
--[[-----------------------------------------
Name:		 GetCivSpecificUnit
Purpose: 	 Get the UnitType for a specific
civ from a UnitClassType.
-------------------------------------------]]
function fnGetCivSpecificUnit( pPly, sUnitClass ) -- Thanks whoward69

	-- BEGIN DEFINES
	local sUnitType = nil
	local sCivType 	= GameInfo.Civilizations[ pPly:GetCivilizationType() ].Type
	-- END DEFINES
	
	--[[ Loop through civilization-specific UnitClass overrides, id est their unique units, and 
	yield to be returned the proper UnitType. --]]
	for pOverride in GameInfo.Civilization_UnitClassOverrides{ CivilizationType = sCivType,
															   UnitClassType = sUnitClass } do
		sUnitType = pOverride.UnitType
		break
	end

	-- If we didn't get anything, yield to be returned the default UnitType for the UnitClass.
	if sUnitType == nil then
		sUnitType = GameInfo.UnitClasses[ sUnitClass ].DefaultUnit
	end

	-- Give whatever function called this the UnitType we yielded.
	print( "Got Civ-Specific UnitType: " .. tostring( sUnitType ) .. ".\n" )
	return sUnitType
end

--[[-----------------------------------------
Name:  		ReturnBestInfantryUnit
Purpose:	Return the best land melee unit
that a city can build.  
-------------------------------------------]]
function fnReturnBestInfantryUnit( pCity )

	--BEGIN DEFINES
	local pPly = pCity:GetOwner()
	
	local possibleUnitClasses = { 
	GameInfoTypes.UNITCLASS_MECH,
	GameInfoTypes.UNITCLASS_MECHANIZED_INFANTRY, 
	GameInfoTypes.UNITCLASS_INFANTRY, 
	GameInfotypes.UNITCLASS_GREAT_WAR_INFANTRY, 
	GameInfoTypes.UNITCLASS_RIFLEMAN, 
	GameInfoTypes.UNITCLASS_MUSKETMAN, 
	GameInfoTypes.UNITCLASS_LONGSWORDSMAN, 
	GameInfoTypes.UNITCLASS_PIKEMAN, 
	GameInfoTypes.UNITCLASS_SWORDSMAN, 
	GameInfoTypes.UNITCLASS_SPEARMAN, 
	GameInfoTypes.UNITCLASS_WARRIOR
	} -- Thanks whoward69
	--END DEFINES
	
	-- Loop through each UnitClassType in the above defined table, see if the city can 
	-- train it's owner's specific UnitType.  Yield to be returned the best land melee 
	-- UnitType the city can build.
	for _, iUnitClass in ipairs( possibleUnitClasses ) do -- Thanks whoward69
		if pCity:CanTrain( fn_GetCivSpecificUnit( pPly, iUnitClass ) ) then
			
			print( "Unit to be given: " .. tostring( fn_GetCivSpecificUnit( pPly, iUnitClass ) .. ".\n" )

			return fn_GetCivSpecificUnit( pPly, iUnitClass )
		end
	end

	-- Uh-oh!
	return -1
end

GameEvents.SetPopulation.Add( function( xOffset, yOffset, fOldPop, fNewPop )

	-- BEGIN DEFINES
	local pPlot				= Map.GetPlot( xOffset, yOffset )
	local pCity  			= pPlot:GetPlotCity()
	local pPly 				= pCity:GetOwner()
	local iUnitMostCurrent	= nil
	local nTrait			= GameInfo.Traits[ pPly ].UnitPerCapitalGrowths
	local sUnitAIType		= GameInfo.UnitAITypes.UNITAI_DEFENSE.ID
	local nZenith			= math.floor( pCity:GetHighestPopulation() )
	local strGrowFallen		= nil
	-- END DEFINES

	--[[ On every growth of every city, run through the following list of conditions:
	1:  Does the city's owner have the trait, and is its population evenly divisible 
	by the trait?
	2:  Is the city a capital?
	3:  Has the city grown instead of shrinking, and is this the city's highest 
	population?

	If all of the above are true, get the best land infantry unit we can, and 
	spawn it at the city. --]]

	if fNewPop > fOldPop then
		strGrowFallen = "grown"
	else
		strGrowFallen = "fallen"
	end

	print( pCity:GetName() .. "'s Population has " .. strGrowFallen .. " from " .. tostring( fOldPop ) .. " to " .. tostring( fNewPop ) .. "!  Highest Population" )

	if ( nTrait > 0 &&
	   ( math.floor( fNewPop ) % nTrait ) == 0 ) then 

	   print( pPly:GetName() .. " has the trait, and the population is divisible by the trait.\n" )

		if pCity:IsCapital() then

			print( pCity:GetName() .. " is the capital of " .. pPly:GetName() .. "'s empire.\n" )

			if ( fNewPop > fOldPop ) &&
			   fNewPop >= nZenith then

			   print( pCity:GetName() "'s current population is more than its previous population, and its population has reached a new level!" )

				iFreeUnit = fnReturnBestInfantryUnit( pCity )

				pPly:AddFreeUnit( iFreeUnit, iUnitAIType ) --[[ What are the 
				implications of this line? --]]
			end
		end
	end
end )

But yeah, I didn't have an entrypoint for it.

On another note; I wonder how hard it would be to make our own entrypoints.
 
On another note; I wonder how hard it would be to make our own entrypoints.

Easy! See the end of InGame.lua or any one of my "UI - Summary Xyz" mods for how to do it

Code:
-- Dynamically load all SummaryBarAddin extensions
function LoadAddins()
  LuaEvents.SummaryBarAddin.Add(OnSummaryBarAddin)

  summaryAddins = {}
  for addin in Modding.GetActivatedModEntryPoints("SummaryBarAddin") do
    local addinFile = addin.File;
    local extension = Path.GetExtension(addinFile);
    local path = string.sub(addinFile, 1, #addinFile - #extension);
    ptr = ContextPtr:LoadNewContext(path)
    table.insert(summaryAddins, ptr)
  end

  table.sort(g_SummaryBarTabs, byPriority)
end
 
Easy! See the end of InGame.lua or any one of my "UI - Summary Xyz" mods for how to do it

Code:
-- Dynamically load all SummaryBarAddin extensions
function LoadAddins()
  LuaEvents.SummaryBarAddin.Add(OnSummaryBarAddin)

  summaryAddins = {}
  for addin in Modding.GetActivatedModEntryPoints("SummaryBarAddin") do
    local addinFile = addin.File;
    local extension = Path.GetExtension(addinFile);
    local path = string.sub(addinFile, 1, #addinFile - #extension);
    ptr = ContextPtr:LoadNewContext(path)
    table.insert(summaryAddins, ptr)
  end

  table.sort(g_SummaryBarTabs, byPriority)
end

I was mainly talking about other entrypoints for things outside of gamedata. Audioscripts and the like.

Also, my logic is broken somehow. I get the print telling me the function was called, but nothing else.
 
Line 62 (missing closing bracket)

Code:
print( "Unit to be given: " .. tostring( fn_GetCivSpecificUnit( pPly, iUnitClass ) .. ".\n" )[COLOR="Red"][B])[/B][/COLOR]

Line 103 and 112 (and not &&)

Code:
if ( nTrait > 0 [COLOR="red"][B]and[/B][/COLOR]
Code:
if ( fNewPop > fOldPop ) [B][COLOR="red"]and[/COLOR][/B]

If you download SciTE you can use the LUA plug-in to "test compile" your code outside of the Civ 5 modding environment
 
Line 62 (missing closing bracket)

Code:
print( "Unit to be given: " .. tostring( fn_GetCivSpecificUnit( pPly, iUnitClass ) .. ".\n" )[COLOR="Red"][B])[/B][/COLOR]

Line 103 and 112 (and not &&)

Code:
if ( nTrait > 0 [COLOR="red"][B]and[/B][/COLOR]
Code:
if ( fNewPop > fOldPop ) [B][COLOR="red"]and[/COLOR][/B]

If you download SciTE you can use the LUA plug-in to "test compile" your code outside of the Civ 5 modding environment

I figured out the part about the &&'s. Missed that closing bracket. It happens. I was just too tired at posting to have a look.

If one of those messed up prints fires, it breaks the entire hook, right? Also, the parenthesis missed is on tostring, not print.

Also, I can't figure out what I'm supposed to do with SciTE.

Code:
print( "Unit to be given: " .. tostring( fn_GetCivSpecificUnit( pPly, iUnitClass ) ) .. ".\n" )

I must have already fixed that.

But anyway, the fact that I'm getting no console errors and my prints show that the hook indeed works and isn't discarded, seems to say that my conditions aren't being evaluated as true.
 
Okay, what the loving hell is going on. For some reason, it seems like nothing's being done after the first line of my hook. Either this, or I'm somehow evaluating a condition that's neither true nor false.

In the instance of neither true nor false, that would mean that I'm getting neither nil nor any thing that is something. Which means I'm getting something that's not even nil, something that's nothing. Maybe a NULL passed up by the game engine? Or maybe it's literally nothing.

Ugh.

Code:
GameEvents.SetPopulation.Add( function( xOffset, yOffset, fOldPop, fNewPop )
	print( "SetPopulation was called." )


	-- BEGIN DEFINES
	local pPlot				= Map.GetPlot( xOffset, yOffset )
	local pCity  			= pPlot:GetPlotCity()
	local iPlyID 			= pCity:GetOwner()
	local pPly				= Players[ iPlyID ] -- Dat Datatype Mismatch, son
	local iUnitMostCurrent	= nil
	local nTrait			= GameInfo.Traits[ pPly ].UnitPerCapitalGrowths
	local sUnitAIType		= GameInfo.UnitAITypes.UNITAI_DEFENSE.ID
	local nZenith			= math.floor( pCity:GetHighestPopulation() )
	local strGrowFallen		= "done something"
	-- END DEFINES

	if xOffset then
		print( "Got an X offset" )
	else
		print( "Don't got an X offset" )
	end

	print( tostring( yOffset ) )
	print( pCity:GetName() )
	print( pPly:GetName() )
	print( tostring( fOldPop ) )
	print( tostring( fNewPop ) )
	print( tostring( nZenith ) )
	

	--[[ On every growth of every city, run through the following list of conditions:
	1:  Does the city's owner have the trait, and is its population evenly divisible 
	by the trait?
	2:  Is the city a capital?
	3:  Has the city grown instead of shrinking, and is this the city's highest 
	population?

	If all of the above are true, get the best land infantry unit we can, and 
	spawn it at the city. --]]

	if ( fNewPop > fOldPop ) then
		strGrowFallen = "grown"
	else
		strGrowFallen = "fallen"
	end

	print( "Info: " .. tostring( pCity:GetName() ) .. "'s Population has " .. strGrowFallen .. " from " .. tostring( fOldPop ) .. " to " .. tostring( fNewPop ) .. "!  Highest Population: " .. tostring( nZenith ) .. ".\n" )

	if ( ( nTrait > 0 ) and ( math.floor( fNewPop ) % nTrait ) == 0 ) then 

	   print( pPly:GetName() .. " has the trait, and the population is divisible by the trait.\n" )

		if pCity:IsCapital() then

			print( pCity:GetName() .. " is the capital of " .. pPly:GetName() .. "'s empire.\n" )

			if ( ( fNewPop > fOldPop ) and ( fNewPop >= nZenith ) ) then

			   print( pCity:GetName() .. "'s current population is more than its previous population, and its population has reached a new level!" )

				iFreeUnit = fnReturnBestInfantryUnit( pCity )

				pPly:AddFreeUnit( iFreeUnit, iUnitAIType ) --[[ What are the 
				implications of this line? --]]
			end
		end
	end
end )

ApGUb.png


So what's the god damn hold-up? As you can see, the hook works. No other prints fire. It's like anything after the first line is taken for gently caress all.

At least GetCivSpecificUnit is working as intended.

LMZrk.png
 
This is wrong

Code:
local nTrait = GameInfo.Traits[ pPly ].UnitPerCapitalGrowths

pPly is a player object, not a trait id nor a trait type
 
This is wrong

Code:
local nTrait = GameInfo.Traits[ pPly ].UnitPerCapitalGrowths

pPly is a player object, not a trait id nor a trait type

I thought that was how I get it for that specific player. How do I do that? I'm not used to this Things.Stuff[Thing].SpecificQuality notation deal.

UnitPerCapitalGrowths is the trait type I intend to check for. I look for it in the Traits table. You know, the thing where you pick the pieces out to form a trait for your civ.

Also, I was figuring on one of my defines being wrong. IT seems prints inserted after the defines do gently caress all.

Just to test, I shifted around other prints to be before the trait define. As a control, Zenith was kept after it.

aYJoc.png


Wonder why the script's not running past that? It seems to just stop running, but the hook isn't thrown out or anything. No error, no nothing. Do I need to set a debug level or something?
 
GameInfo.SOMETHING[] only works for database tables that have both an ID and a Type field, so even if UnitAITypes was a database table (which it isn't, as it's Unit_AITypes) it wouldn't work anyway
 
GameInfo.SOMETHING[] only works for database tables that have both an ID and a Type field, so even if UnitAITypes was a database table (which it isn't, as it's Unit_AITypes) it wouldn't work anyway

So uh, back to my question. How do I get access a player's traits table and the data within?
 
So uh, back to my question. How do I get access a player's traits table and the data within?

http://modiki.civfanatics.com/index.php/DB.Query_(Civ5_API)

Looks like you can use this to execute an SQL query and check the traits table for whatever you are looking for. i.e.

Code:
for row in DB.Query("SELECT Type, UnitCapitalGrowths FROM Traits) do
   --logic
end

Also, judging from the modiki, there's no way to get a players trait directly. (After all, Leader_Traits doesn't have ID or Type.

If I had to guess:

Code:
iLeader = pPly:GetLeaderType()
--LeaderType returns integer, which I believe correspons to the rowid, not certain, see here:
--http://modiki.civfanatics.com/index.php/LeaderType_(Civ5_Type)
tLeader = DB.Query(SELECT LeaderType FROM Civilization_Leaders WHERE rowID = iLeader)
--I'm not sure, but I think that returns a string. then you have to check it against Leader_Traits
tTrait = DB.Query(SELECT TraitType FROM Leader_Traits WHERE LeaderType = tLeader)
--then
for row in DB.Query(SELECT UnitCapitalGrowths FROM Traits WHERE Type = tTrait) do
   if UnitCapitalGrowths yada yada yada
end

That's my guess. Full disclosure: I am an idiot, so that may be wrong.
 
there's no way to get a players trait directly.

Just use SQL to perform a join from Civilizations to Leaders to Traits

Code:
SELECT t.Type
 FROM Civilizations c, Civilization_Leaders cl, Leaders l, Leader_Traits lt, Traits t
 WHERE c.Type='CIVILIZATION_ENGLAND'
   AND c.Type=cl.CivilizationType
   AND cl.LeaderheadType = l.Type
   AND l.type = lt.LeaderType
   AND lt.TraitType = t.Type;
 
http://modiki.civfanatics.com/index.php/DB.Query_(Civ5_API)

Looks like you can use this to execute an SQL query and check the traits table for whatever you are looking for. i.e.

Code:
for row in DB.Query("SELECT Type, UnitCapitalGrowths FROM Traits) do
   --logic
end

Also, judging from the modiki, there's no way to get a players trait directly. (After all, Leader_Traits doesn't have ID or Type.

If I had to guess:

Code:
iLeader = pPly:GetLeaderType()
--LeaderType returns integer, which I believe correspons to the rowid, not certain, see here:
--http://modiki.civfanatics.com/index.php/LeaderType_(Civ5_Type)
tLeader = DB.Query(SELECT LeaderType FROM Civilization_Leaders WHERE rowID = iLeader)
--I'm not sure, but I think that returns a string. then you have to check it against Leader_Traits
tTrait = DB.Query(SELECT TraitType FROM Leader_Traits WHERE LeaderType = tLeader)
--then
for row in DB.Query(SELECT UnitCapitalGrowths FROM Traits WHERE Type = tTrait) do
   if UnitCapitalGrowths yada yada yada
end

That's my guess. Full disclosure: I am an idiot, so that may be wrong.

Aren't Leader_Traits and Traits two completely different tables?
 
Code:
tLeader = DB.Query(SELECT LeaderType FROM Civilization_Leaders WHERE rowID = iLeader)
That line won't work as you have to iterate all DB.Query() calls (as the latter code showed)
 
Just use SQL to perform a join from Civilizations to Leaders to Traits

Code:
SELECT t.Type
 FROM Civilizations c, Civilization_Leaders cl, Leaders l, Leader_Traits lt, Traits t
 WHERE c.Type='CIVILIZATION_ENGLAND'
   AND c.Type=cl.CivilizationType
   AND cl.LeaderheadType = l.Type
   AND l.type = lt.LeaderType
   AND lt.TraitType = t.Type;

completely lost here. Not even gonna lie, this isn't amking any sense. Also, is this civ specific?

this is why we need file.Write so we can write SQL scripts on the fly for each civilization.
 
Back
Top Bottom