Gaining a free Unit upon the discovery of a Tech?

Keniisu

YouTuber | Modder
Joined
Dec 17, 2015
Messages
501
Location
United States
Would this LUA work for such a UA?
Code:
function PlayerTurnProcessing(iPlayer)
	local iPlayerCiv = pPlayer:GetCivilizationType()
	local iAmericaCiv = GameInfoTypes.CIVILIZATION_MODERN_AMERICA
	if iPlayerCiv == iAmericaCiv
		-- BEGINNING OF THE LUA CODE ---
		local pPlayer = Player[iPlayer]
		local pTeam = Teams[iTeam]			
		local pCapital = pPlayer:GetCapitalCity()
		local pPlot = pCapital:Plot()
		-- UNIT TYPES ---
		local eUnitType = UNIT_SWORDSMAN
		local eUnitType2 = UNIT_HORSEMAN
		-- IF HORSEBACK IS DISCOVERED --
		if return pTeam:HasTech(TECH_HORSEBACK_RIDING);
			if pPlayer:GetCivilizationType() == GameInfoTypes["CIVILIZATION_MODERN_AMERICA"]
				local pNewUnit = pPlayer:InitUnit(GameInfoTypes[eUnitType2], pPlot:GetX(), pPlot:GetY())
				if not (pPlayer:IsHuman()) then
					pNewUnit:JumpToNearestValidPlot()
				end
			end
		end
		-- IF IRON_WORKING IS DISCOVERED --
		if return pTeam:HasTech(TECH_IRON_WORKING);
			if pPlayer:GetCivilizationType() == GameInfoTypes["CIVILIZATION_MODERN_AMERICA"]
				local pNewUnit = pPlayer:InitUnit(GameInfoTypes[eUnitType], pPlot:GetX(), pPlot:GetY())
				if not (pPlayer:IsHuman()) then
					pNewUnit:JumpToNearestValidPlot()
				end
			end
		end
	end
end
 
No need to use Lua for a free unit upon tech discovery. There are XML tags for that (look Venice's Civ files).
I'm trying to make it give a free unit based on multiple Tech Discoveries so I felt LUA would be slightly easier than XML. (Swordsman when Iron Working is discovered. Horseman when Horseback Riding is discovered. etc.) Also I wanted to know if it's possible to check for strategic resources as well before giving the free unit?
 
Would this LUA work for such a UA?
Code:
function PlayerTurnProcessing(iPlayer)
	local iPlayerCiv = pPlayer:GetCivilizationType()
	local iAmericaCiv = GameInfoTypes.CIVILIZATION_MODERN_AMERICA
	if iPlayerCiv == iAmericaCiv
		-- BEGINNING OF THE LUA CODE ---
		local pPlayer = Player[iPlayer]
		local pTeam = Teams[iTeam]			
		local pCapital = pPlayer:GetCapitalCity()
		local pPlot = pCapital:Plot()
		-- UNIT TYPES ---
		local eUnitType = UNIT_SWORDSMAN
		local eUnitType2 = UNIT_HORSEMAN
		-- IF HORSEBACK IS DISCOVERED --
		if return pTeam:HasTech(TECH_HORSEBACK_RIDING);
			if pPlayer:GetCivilizationType() == GameInfoTypes["CIVILIZATION_MODERN_AMERICA"]
				local pNewUnit = pPlayer:InitUnit(GameInfoTypes[eUnitType2], pPlot:GetX(), pPlot:GetY())
				if not (pPlayer:IsHuman()) then
					pNewUnit:JumpToNearestValidPlot()
				end
			end
		end
		-- IF IRON_WORKING IS DISCOVERED --
		if return pTeam:HasTech(TECH_IRON_WORKING);
			if pPlayer:GetCivilizationType() == GameInfoTypes["CIVILIZATION_MODERN_AMERICA"]
				local pNewUnit = pPlayer:InitUnit(GameInfoTypes[eUnitType], pPlot:GetX(), pPlot:GetY())
				if not (pPlayer:IsHuman()) then
					pNewUnit:JumpToNearestValidPlot()
				end
			end
		end
	end
end
  1. This
    Code:
    if iPlayerCiv == iAmericaCiv
    has no 'then':
    Code:
    if iPlayerCiv == iAmericaCiv [color="red"]then[/color]
  2. This
    Code:
    local pPlayer = Player[iPlayer]
    is missing a required 's':
    Code:
    local pPlayer = Player[color="red"]s[/color][iPlayer]
  3. This
    Code:
    local pTeam = Teams[iTeam]
    is referencing a non-existant variable called 'iTeam' :
    Code:
    local pTeam = Teams[[color="red"]pPlayer:GetTeam()[/color]]
  4. This
    Code:
    local eUnitType = UNIT_SWORDSMAN
    will interpret "UNIT_SWORDSMAN" as an undefined variable, so will be set to 'nil' :
    Code:
    local eUnitType = [color="red"]GameInfoTypes.UNIT_SWORDSMAN[/color]
  5. This
    Code:
    local eUnitType2 = UNIT_HORSEMAN
    will interpret "UNIT_HORSEMAN" as an undefined variable, so will be set to 'nil' :
    Code:
    local eUnitType2 = [color="red"]GameInfoTypes.UNIT_HORSEMAN[/color]
  6. This
    Code:
    if return pTeam:HasTech(TECH_HORSEBACK_RIDING);
    is:
    • missing the needed 'then'
    • incorrectly structured.
      • Never use the word 'return' in an lua function unless you want the function to terminate at that line of code and "return" processing to wherever it originated. In the case of Hook events, such as GameEvents.PlayerDoTurn, this means end the function.
    • is going to evaluate "TECH_HORSEBACK_RIDING" as an undefined variable, and therefore 'nil', which in turn means the conditional line can never be seen as being 'true'.
    • Is using an incorrect method, it needs to be Team:IsHasTech(iTechForCheck)
    Code:
    if pTeam:IsHasTech(GameInfoTypes.TECH_HORSEBACK_RIDING) then
  7. This
    Code:
    if pPlayer:GetCivilizationType() == GameInfoTypes["CIVILIZATION_MODERN_AMERICA"]
    has already been conducted earlier in the code so is not needed. It is also missing the required 'then' at the end of the line.
  8. This
    Code:
    local pNewUnit = pPlayer:InitUnit(GameInfoTypes[eUnitType2], pPlot:GetX(), pPlot:GetY())
    needs to be redrafted to account for the ealier alterations needed to the definition of variable "eUnitType2", and so needs to become:
    Code:
    local pNewUnit = pPlayer:InitUnit(eUnitType2, pPlot:GetX(), pPlot:GetY())
  9. This
    Code:
    if return pTeam:HasTech(TECH_IRON_WORKING);
    is a repeat of the problem with "TECH_HORSEBACK_RIDING"
  10. This second
    Code:
    if pPlayer:GetCivilizationType() == GameInfoTypes["CIVILIZATION_MODERN_AMERICA"]
    is also unneeded and is also missing the needed 'then'
  11. This
    Code:
    local pNewUnit = pPlayer:InitUnit(GameInfoTypes[eUnitType], pPlot:GetX(), pPlot:GetY())
    needs the same sort of redraft for the same reasons:
    Code:
    local pNewUnit = pPlayer:InitUnit(eUnitType, pPlot:GetX(), pPlot:GetY())
  12. This
    Code:
    if not (pPlayer:IsHuman()) then
    	pNewUnit:JumpToNearestValidPlot()
    end
    is fine so far as it goes, but when the player is human, all that happens with this sort of construction is that the unit initialization is placed into a local variable, and then the unit is never actually created:
    Code:
    local pNewUnit = pPlayer:InitUnit(eUnitType, pPlot:GetX(), pPlot:GetY())
    if not (pPlayer:IsHuman()) then
    	pNewUnit:JumpToNearestValidPlot()
    end
  13. The larger problem however is that since PlayerDoTurn functions run literally every turn, the player will get a new Horseman and a new Swordsman every turn after they have discovered both Iron Working and Horseback Riding, and they will get one or the other of the units as soon as they discover one or the other of Iron Working or Horseback Riding, and will continue to do so for the remainder of the game.
  14. In order to get to the concept you want (as I understand it), you really need to use GameEvents.TeamTechResearched instead of GameEvents.PlayerDoTurn. Functions added to TeamTechResearched will only run at the instant the Tech is discovered, and not otherwise, so long as you base everything within the function on whether or not the tech just discovered is Horseback Riding or Iron Working, and whether or not the 'Team' that just researched the tech is the team for your civilization.

[edit]
  • Bane_ caught the issue with local pPlayer being used before it was defined, which I missed.
    [edit edit]He removed too much stuff from his post. This:
    Code:
    function PlayerTurnProcessing(iPlayer)
    	local iPlayerCiv = pPlayer:GetCivilizationType()
    	local iAmericaCiv = GameInfoTypes.CIVILIZATION_MODERN_AMERICA
    	if iPlayerCiv == iAmericaCiv
    		-- BEGINNING OF THE LUA CODE ---
    		local pPlayer = Player[iPlayer]
    needs to be:
    Code:
    function PlayerTurnProcessing(iPlayer)
    	local pPlayer = Player[COLOR="Blue"][B]s[/B][/COLOR][iPlayer]
    	local iPlayerCiv = pPlayer:GetCivilizationType()
    	local iAmericaCiv = GameInfoTypes.CIVILIZATION_MODERN_AMERICA
    	if iPlayerCiv == iAmericaCiv [COLOR="Blue"][B]then[/B][/COLOR]
    		-- BEGINNING OF THE LUA CODE ---
  • His method for dealing with UNIT_HORSEMAN and UNIT_SWORDSMAN will also work, as opposed to my two-step method -- but it must be his method for all usage of UNIT_HORSEMAN and UNIT_SWORDSMAN or my method, not a mixture of our two methods.
  • But the method I outlined for the technology checks will need to be as I have shown
 
I'm trying to make it give a free unit based on multiple Tech Discoveries so I felt LUA would be slightly easier than XML.

Create multiple traits, each one giving one unit type on discovery of the associated tech. Give the leader each of the traits.

Leaders can have multiple traits, and free units associated with techs are cumulative (and not replacements)
 
Create multiple traits, each one giving one unit type on discovery of the associated tech. Give the leader each of the traits.

Leaders can have multiple traits, and free units associated with techs are cumulative (and not replacements)

Leaders can have multiple traits? Iiiiiiinteresting~
 
Leaders can have multiple traits? Iiiiiiinteresting~

Think of traits as leader promotions. The game core takes all traits and adds all the effects together to form one mega-trait. There are a few quirks (a bit like multiple copies of a building not combining to add tourism to a city) but they are very few.

What's more, traits can be enabled/disabled by techs (and if you're using a modded DLL by policies and beliefs), and the "mega-trait" is updated as techs(/policies/beliefs) are gained/lost

(Look at the Mayan Calendar trait, the leader actually has no trait until Theology is discovered, then they get their abilities)

Enjoy!

W
 
I assume I have to define pPlayers and pTeam before the function.
Code:
local pPlayer = Players[iPlayer]
local pTeam = Teams[pPlayer:GetTeam()]	

function GameEvents.TeamTechResearched(pTeam, TECH_HORSEBACK_RIDING,) 
	local iPlayerCiv = pPlayer:GetCivilizationType()
	local iAmericaCiv = GameInfoTypes.CIVILIZATION_MODERN_AMERICA
	if iPlayerCiv == iAmericaCiv then
		-- BEGINNING OF THE LUA CODE ---		
		local pCapital = pPlayer:GetCapitalCity()
		local pPlot = pCapital:Plot()
		-- UNIT TYPES ---
		local eUnitType = GameInfoTypes.UNIT_HORSEMAN
		-- IF HORSEBACK IS DISCOVERED --
		if pTeam:IsHasTech(GameInfoTypes.TECH_HORSEBACK_RIDING) then
			local pNewUnit = pPlayer:InitUnit(eUnitType, pPlot:GetX(), pPlot:GetY())
			if not (pPlayer:IsHuman()) then
				pNewUnit:JumpToNearestValidPlot()
			end
		end
	end
end
function GameEvents.TeamTechResearched(pTeam, TECH_IRON_WORKING,) 
	local iPlayerCiv = pPlayer:GetCivilizationType()
	local iAmericaCiv = GameInfoTypes.CIVILIZATION_MODERN_AMERICA
	if iPlayerCiv == iAmericaCiv then
		-- BEGINNING OF THE LUA CODE ---		
		local pCapital = pPlayer:GetCapitalCity()
		local pPlot = pCapital:Plot()
		-- UNIT TYPES ---
		local eUnitType2 =  GameInfoTypes.UNIT_SWORDSMAN
		-- IF HORSEBACK IS DISCOVERED --
		if pTeam:IsHasTech(GameInfoTypes.TECH_IRON_WORKING) then
			local pNewUnit = pPlayer:InitUnit(eUnitType2, pPlot:GetX(), pPlot:GetY())
			if not (pPlayer:IsHuman()) then
				pNewUnit:JumpToNearestValidPlot()
			end
		end
	end
end
 
I assume I have to define pPlayers and pTeam before the function.
Code:
local pPlayer = Players[iPlayer]
local pTeam = Teams[pPlayer:GetTeam()]	

function GameEvents.TeamTechResearched(pTeam, TECH_HORSEBACK_RIDING,) 
	local iPlayerCiv = pPlayer:GetCivilizationType()
	local iAmericaCiv = GameInfoTypes.CIVILIZATION_MODERN_AMERICA
	if iPlayerCiv == iAmericaCiv then
		-- BEGINNING OF THE LUA CODE ---		
		local pCapital = pPlayer:GetCapitalCity()
		local pPlot = pCapital:Plot()
		-- UNIT TYPES ---
		local eUnitType = GameInfoTypes.UNIT_HORSEMAN
		-- IF HORSEBACK IS DISCOVERED --
		if pTeam:IsHasTech(GameInfoTypes.TECH_HORSEBACK_RIDING) then
			local pNewUnit = pPlayer:InitUnit(eUnitType, pPlot:GetX(), pPlot:GetY())
			if not (pPlayer:IsHuman()) then
				pNewUnit:JumpToNearestValidPlot()
			end
		end
	end
end
function GameEvents.TeamTechResearched(pTeam, TECH_IRON_WORKING,) 
	local iPlayerCiv = pPlayer:GetCivilizationType()
	local iAmericaCiv = GameInfoTypes.CIVILIZATION_MODERN_AMERICA
	if iPlayerCiv == iAmericaCiv then
		-- BEGINNING OF THE LUA CODE ---		
		local pCapital = pPlayer:GetCapitalCity()
		local pPlot = pCapital:Plot()
		-- UNIT TYPES ---
		local eUnitType2 =  GameInfoTypes.UNIT_SWORDSMAN
		-- IF HORSEBACK IS DISCOVERED --
		if pTeam:IsHasTech(GameInfoTypes.TECH_IRON_WORKING) then
			local pNewUnit = pPlayer:InitUnit(eUnitType2, pPlot:GetX(), pPlot:GetY())
			if not (pPlayer:IsHuman()) then
				pNewUnit:JumpToNearestValidPlot()
			end
		end
	end
end
  1. Every game 'hook' event has its own preset list of paramters that is passed to any function that is made to run from that individual hook event.
  2. GameEvents.TeamTechResearched passes the following parameters in the following order: iTeam, iTech, iChange
  3. So, in order to monitor whether an individual player has researched Iron Working or Horseback Riding, we want our basic code as follows:
    Code:
    function PlayerResearchComplete(iTeam, iTech, iChange)
    	--do stuff when any player has researched a tech
    end
    GameEvents.TeamTechResearched.Add(PlayerResearchComplete)
  4. In order to limit the code to only run when any player researches Iron Working or Horseback Riding (it will actually be a team because players don't actually 'own' technologies: Teams do) we need as follows:
    Code:
    function PlayerResearchComplete(iTeam, iTech, iChange)
    	if (iTech == GameInfoTypes.TECH_IRON_WORKING) or (iTech == GameInfoTypes.TECH_HORSEBACK_RIDING) then
    		--do stuff here when one of these two techs has been researched by any player
    	end
    end
    GameEvents.TeamTechResearched.Add(PlayerResearchComplete)
  5. To then determine who researched the tech, we need to use the data from parameter "iTeam" and translate that into a usable "player" form:
    Code:
    function PlayerResearchComplete(iTeam, iTech, iChange)
    	if (iTech == GameInfoTypes.TECH_IRON_WORKING) or (iTech == GameInfoTypes.TECH_HORSEBACK_RIDING) then
    		for iPlayerID = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
    			local pScanPlayer = Players[iPlayerID]
    			if pScanPlayer:IsAlive() and pScanPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_MODERN_AMERICA then
    				if pScanPlayer:GetTeam() == iTeam then
    					-- we know that the player we are interested in just researched Iron Working of Horseback Riding, so we want to do our stuff here
    				end
    			end
    		end
    
    	end
    end
    GameEvents.TeamTechResearched.Add(PlayerResearchComplete)
  6. Now we need to get the player's capital city, and then determine which of the two techs was researched, and to simplify things a little we will also create a generic "iUnitType" variable:
    Code:
    function PlayerResearchComplete(iTeam, iTech, iChange)
    	if (iTech == GameInfoTypes.TECH_IRON_WORKING) or (iTech == GameInfoTypes.TECH_HORSEBACK_RIDING) then
    		for iPlayerID = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
    			local pScanPlayer = Players[iPlayerID]
    			if pScanPlayer:IsAlive() and pScanPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_MODERN_AMERICA then
    				if pScanPlayer:GetTeam() == iTeam then
    					local pCapital = pScanPlayer:GetCapitalCity()
    					local iUnitType
    					if (iTech == GameInfoTypes.TECH_IRON_WORKING) then
    						iUnitType = GameInfoTypes.UNIT_SWORDSMAN
    					elseif (iTech == GameInfoTypes.TECH_HORSEBACK_RIDING) then
    						iUnitType = GameInfoTypes.UNIT_HORSEMAN
    					end
    				end
    			end
    		end
    
    	end
    end
    GameEvents.TeamTechResearched.Add(PlayerResearchComplete)
    At the moment we only have two techs we are interested in, and we can only get to this part of the code if the tech just researched was one of the ones we are interested in:
    Code:
    if (iTech == GameInfoTypes.TECH_IRON_WORKING) then
    	iUnitType = GameInfoTypes.UNIT_SWORDSMAN
    elseif (iTech == GameInfoTypes.TECH_HORSEBACK_RIDING) then
    	iUnitType = GameInfoTypes.UNIT_HORSEMAN
    end
    We could also have used "else" instead of "elseif", but by using an "elseif" structure it may be easier for someone to understand how to add additional techs to this section of the code.
  7. We now need the code to spit out the units into the game:
    Code:
    function PlayerResearchComplete(iTeam, iTech, iChange)
    	if (iTech == GameInfoTypes.TECH_IRON_WORKING) or (iTech == GameInfoTypes.TECH_HORSEBACK_RIDING) then
    		for iPlayerID = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
    			local pScanPlayer = Players[iPlayerID]
    			if pScanPlayer:IsAlive() and pScanPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_MODERN_AMERICA then
    				if pScanPlayer:GetTeam() == iTeam then
    					local pCapital = pScanPlayer:GetCapitalCity()
    					local iUnitType
    					if (iTech == GameInfoTypes.TECH_IRON_WORKING) then
    						iUnitType = GameInfoTypes.UNIT_SWORDSMAN
    					elseif (iTech == GameInfoTypes.TECH_HORSEBACK_RIDING) then
    						iUnitType = GameInfoTypes.UNIT_HORSEMAN
    					end
    					local pNewUnit = pScanPlayer:InitUnit(iUnitType, pCapital:GetX(), pCapital:GetY())
    					if (pNewUnit and pCapital:Plot():GetNumUnits() > 1) then
    						pNewUnit:JumpToNearestValidPlot()
    					else
    						pNewUnit:SetExperience(0)
    					end
      					return
    				end
    			end
    		end
    
    	end
    end
    GameEvents.TeamTechResearched.Add(PlayerResearchComplete)
    The "return" is added where it is because at that point we are done and we may as well end execution of the function.
  8. There are some streamlining methods that could be employed, but sometimes demonstrating them is less help to a newer mod-maker than a 'brute-force' method that works even if a little inefficient.
 
I have an inquiry related to this thread:

I am currently programming in a UA for a Civ that uses this ability, but to a greater degree. Part of their UA is: "When you discover a Technology that unlocks a Military Unit, receive 2 free copies of that unit outside the Capital."

Now I've programmed pretty much all of the game's basic units for this UA, but there are some hiccups:
1. When the technology unlocks more than one unit, I can't seem to get more than just the first one I've listed to spawn near the capital.
2. When spawning the units, only 1 units of the spawn instead of the required 2. I'm not sure how to change the amount that will spawn.
3. (This one's my bad). Some of the entries below may be mislabeled, I'm certain I can fix that, so no need to worry about it if you stumble upon any typos. Of course, pointing them out would be nice, should there be any.

Here's my code below: (It's kind of long)
Spoiler :
Code:
function AmonResearchComplete(iTeam, iTech, iChange)
	if (iTech == GameInfoTypes.TECH_ARCHERY) or (iTech == GameInfoTypes.TECH_SAILING) or (iTech == GameInfoTypes.TECH_THE_WHEEL) or (iTech == GameInfoTypes.TECH_BRONZE_WORKING) or (iTech == GameInfoTypes.TECH_HORSEBACK_RIDING) or (iTech == GameInfoTypes.TECH_MATHEMATICS) or (iTech == GameInfoTypes.TECH_CONSTRUCTION) or (iTech == GameInfoTypes.TECH_IRON_WORKING) or (iTech == GameInfoTypes.TECH_CIVIL_SERVICE) or (iTech == GameInfoTypes.TECH_COMPASS) or (iTech == GameInfoTypes.TECH_CHIVALRY) or (iTech == GameInfoTypes.TECH_MACHINERY) or (iTech == GameInfoTypes.TECH_PHYSICS) or (iTech == GameInfoTypes.TECH_STEEL) or (iTech == GameInfoTypes.TECH_ASTRONOMY) or (iTech == GameInfoTypes.TECH_GUNPOWDER) or (iTech == GameInfoTypes.TECH_NAVIGATION) or (iTech == GameInfoTypes.TECH_METALLURGY) or (iTech == GameInfoTypes.TECH_CHEMISTRY) or (iTech == GameInfoTypes.TECH_INDUSTRIALIZATION) or (iTech == GameInfoTypes.TECH_RIFLING) or (iTech == GameInfoTypes.TECH_MILITARY_SCIENCE) or (iTech == GameInfoTypes.TECH_STEAM_POWER) or (iTech == GameInfoTypes.TECH_DYNAMITE) or (iTech == GameInfoTypes.TECH_REFRIGERATION) or (iTech == GameInfoTypes.TECH_REPLACEABLE_PARTS) or (iTech == GameInfoTypes.TECH_FLIGHT) or (iTech == GameInfoTypes.TECH_PLASTICS) or (iTech == GameInfoTypes.TECH_ELECTRONICS) or (iTech == GameInfoTypes.TECH_BALLISTICS) or (iTech == GameInfoTypes.TECH_COMBUSTION) or (iTech == GameInfoTypes.TECH_PENICILLIN) or (iTech == GameInfoTypes.TECH_RADAR) or (iTech == GameInfoTypes.TECH_COMBINED_ARMS) or (iTech == GameInfoTypes.TECH_NUCLEAR_FISSION) or (iTech == GameInfoTypes.TECH_ROCKETRY) or (iTech == GameInfoTypes.TECH_COMPUTERS) or (iTech == GameInfoTypes.TECH_TELECOM) or (iTech == GameInfoTypes.TECH_MOBILE_TACTICS) or (iTech == GameInfoTypes.TECH_ADVANCED_BALLISTICS) or (iTech == GameInfoTypes.TECH_ROBOTICS) or (iTech == GameInfoTypes.TECH_LASERS) or (iTech == GameInfoTypes.TECH_NUCLEAR_FUSION) or (iTech == GameInfoTypes.TECH_NANOTECHNOLOGY) or (iTech == GameInfoTypes.TECH_STEALTH) then
		for iPlayerID = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
			local pScanPlayer = Players[iPlayerID]
			if pScanPlayer:IsAlive() and pScanPlayer:GetCivilizationType() == GameInfoTypes.CIVILIZATION_AMERICA then
				if pScanPlayer:GetTeam() == iTeam then
					local pCapital = pScanPlayer:GetCapitalCity()
					local iUnitType
					if (iTech == GameInfoTypes.TECH_ARCHERY) then
						iUnitType = GameInfoTypes.UNIT_ARCHER
					elseif (iTech == GameInfoTypes.TECH_SAILING) then
						iUnitType = GameInfoTypes.UNIT_TRIREME
					elseif (iTech == GameInfoTypes.TECH_THE_WHEEL) then
						iUnitType = GameInfoTypes.UNIT_CHARIOT_ARCHER
					elseif (iTech == GameInfoTypes.TECH_BRONZE_WORKING) then
						iUnitType = GameInfoTypes.UNIT_SPEARMAN
					elseif (iTech == GameInfoTypes.TECH_HORSEBACK_RIDING) then
						iUnitType = GameInfoTypes.UNIT_HORSEMAN
					elseif (iTech == GameInfoTypes.TECH_MATHEMATICS) then
						iUnitType = GameInfoTypes.UNIT_CATAPULT
					elseif (iTech == GameInfoTypes.TECH_CONSTRUCTION) then
						iUnitType = GameInfoTypes.UNIT_COMPOSITE_BOWMAN
					elseif (iTech == GameInfoTypes.TECH_IRON_WORKING) then
						iUnitType = GameInfoTypes.UNIT_AMERICA_CHI_BLOCKER
					elseif (iTech == GameInfoTypes.TECH_CIVIL_SERVICE) then
						iUnitType = GameInfoTypes.UNIT_PIKEMAN
					elseif (iTech == GameInfoTypes.TECH_CIVIL_SERVICE) then
						iUnitType2 = GameInfoTypes.UNIT_LANDSKNECHT
					elseif (iTech == GameInfoTypes.TECH_COMPASS) then
						iUnitType = GameInfoTypes.UNIT_GALLEASS
					elseif (iTech == GameInfoTypes.TECH_CHIVALRY) then
						iUnitType = GameInfoTypes.UNIT_KNIGHT
					elseif (iTech == GameInfoTypes.TECH_MACHINERY) then
						iUnitType = GameInfoTypes.UNIT_CROSSBOWMAN
					elseif (iTech == GameInfoTypes.TECH_PHYSICS) then
						iUnitType = GameInfoTypes.UNIT_TREBUCHET
					elseif (iTech == GameInfoTypes.TECH_STEEL) then
						iUnitType = GameInfoTypes.UNIT_LONGSWORDSMAN
					elseif (iTech == GameInfoTypes.TECH_ASTRONOMY) then
						iUnitType = GameInfoTypes.UNIT_CARAVEL
					elseif (iTech == GameInfoTypes.TECH_GUNPOWDER) then
						iUnitType = GameInfoTypes.UNIT_MUSKETMAN
					elseif (iTech == GameInfoTypes.TECH_NAVIGATION) then
						iUnitType = GameInfoTypes.UNIT_PRIVATEER
					elseif (iTech == GameInfoTypes.TECH_NAVIGATION) then
						iUnitType2 = GameInfoTypes.UNIT_FRIGATE
					elseif (iTech == GameInfoTypes.TECH_METALLURGY) then
						iUnitType = GameInfoTypes.UNIT_LANCER
					elseif (iTech == GameInfoTypes.TECH_CHEMISTRY) then
						iUnitType = GameInfoTypes.UNIT_CANNON
					elseif (iTech == GameInfoTypes.TECH_INDUSTRIALIZATION) then
						iUnitType = GameInfoTypes.UNIT_GATLING_GUN
					elseif (iTech == GameInfoTypes.TECH_RIFLING) then
						iUnitType = GameInfoTypes.UNIT_RIFLEMAN
					elseif (iTech == GameInfoTypes.TECH_MILITARY_SCIENCE) then
						iUnitType = GameInfoTypes.UNIT_CAVALRY
					elseif (iTech == GameInfoTypes.TECH_STEAM_POWER) then
						iUnitType = GameInfoTypes.UNIT_IRONCLAD
					elseif (iTech == GameInfoTypes.TECH_DYNAMITE) then
						iUnitType = GameInfoTypes.UNIT_ARTILLERY
					elseif (iTech == GameInfoTypes.TECH_REFRIGERATION) then
						iUnitType = GameInfoTypes.UNIT_SUBMARINE
					elseif (iTech == GameInfoTypes.TECH_REPLACEABLE_PARTS) then
						iUnitType = GameInfoTypes.UNIT_GREAT_WAR_INFANTRY
					elseif (iTech == GameInfoTypes.TECH_FLIGHT) then
						iUnitType = GameInfoTypes.UNIT_TRIPLANE
					elseif (iTech == GameInfoTypes.TECH_FLIGHT) then
						iUnitType = GameInfoTypes.UNIT_WWI_BOMBER
					elseif (iTech == GameInfoTypes.TECH_PLASTICS) then
						iUnitType = GameInfoTypes.UNIT_INFANTRY
					elseif (iTech == GameInfoTypes.TECH_ELECTRONICS) then
						iUnitType = GameInfoTypes.UNIT_CARRIER
					elseif (iTech == GameInfoTypes.TECH_ELECTRONICS) then
						iUnitType2 = GameInfoTypes.UNIT_BATTLESHIP
					elseif (iTech == GameInfoTypes.TECH_BALLISTICS) then
						iUnitType = GameInfoTypes.UNIT_ANTI_AIRCRAFT_GUN
					elseif (iTech == GameInfoTypes.TECH_BALLISTICS) then
						iUnitType2 = GameInfoTypes.UNIT_MACHINE_GUN
					elseif (iTech == GameInfoTypes.TECH_COMBUSTION) then
						iUnitType = GameInfoTypes.UNIT_DESTROYER
					elseif (iTech == GameInfoTypes.TECH_COMBUSTION) then
						iUnitType2 = GameInfoTypes.UNIT_AMERICA_MECHA_TANK
					elseif (iTech == GameInfoTypes.TECH_PENICILLIN) then
						iUnitType = GameInfoTypes.UNIT_MARINE
					elseif (iTech == GameInfoTypes.TECH_RADAR) then
						iUnitType = GameInfoTypes.UNIT_FIGHTER
					elseif (iTech == GameInfoTypes.TECH_RADAR) then
						iUnitType2 = GameInfoTypes.UNIT_BOMBER
					elseif (iTech == GameInfoTypes.TECH_COMBINED_ARMS) then
						iUnitType = GameInfoTypes.UNIT_TANK
					elseif (iTech == GameInfoTypes.TECH_COMBINED_ARMS) then
						iUnitType2 = GameInfoTypes.UNIT_ANTI_TANK_GUN
					elseif (iTech == GameInfoTypes.TECH_NUCLEAR_FISSION) then
						iUnitType = GameInfoTypes.UNIT_ATOMIC_BOMB
					elseif (iTech == GameInfoTypes.TECH_NUCLEAR_FISSION) then
						iUnitType2 = GameInfoTypes.UNIT_BAZOOKA
					elseif (iTech == GameInfoTypes.TECH_ROCKETRY) then
						iUnitType = GameInfoTypes.UNIT_MOBILE_SAM
					elseif (iTech == GameInfoTypes.TECH_ROCKETRY) then
						iUnitType2 = GameInfoTypes.UNIT_ROCKET_ARTILLERY
					elseif (iTech == GameInfoTypes.TECH_COMPUTERS) then
						iUnitType = GameInfoTypes.UNIT_HELICOPTER_GUNSHIP
					elseif (iTech == GameInfoTypes.TECH_TELECOM) then
						iUnitType = GameInfoTypes.UNIT_NUCLEAR_SUNMARINE
					elseif (iTech == GameInfoTypes.TECH_MOBILE_TACTICS) then
						iUnitType = GameInfoTypes.UNIT_MECHANIZED_INFANTRY
					elseif (iTech == GameInfoTypes.TECH_ADVANCED_BALLISTICS) then
						iUnitType = GameInfoTypes.UNIT_NUCLEAR_MISSILE
					elseif (iTech == GameInfoTypes.TECH_ADVANCED_BALLISTICS) then
						iUnitType2 = GameInfoTypes.UNIT_GUIDED_MISSILE
					elseif (iTech == GameInfoTypes.TECH_ROBOTICS) then
						iUnitType = GameInfoTypes.UNIT_MISSILE_CRUISER
					elseif (iTech == GameInfoTypes.TECH_LASERS) then
						iUnitType = GameInfoTypes.UNIT_JET_FIGHTER
					elseif (iTech == GameInfoTypes.TECH_LASERS) then
						iUnitType2 = GameInfoTypes.UNIT_MODERN_ARMOR
					elseif (iTech == GameInfoTypes.TECH_NUCLEAR_FUSION) then
						iUnitType = GameInfoTypes.UNIT_MECH
					elseif (iTech == GameInfoTypes.TECH_NANOTECHNOLOGY) then
						iUnitType = GameInfoTypes.UNIT_XCOM_SQUAD
					elseif (iTech == GameInfoTypes.TECH_STEALTH) then
						iUnitType = GameInfoTypes.UNIT_STEALTH_BOMBER
					end
					local pNewUnit = pScanPlayer:InitUnit(iUnitType, pCapital:GetX(), pCapital:GetY())
					if (pNewUnit and pCapital:Plot():GetNumUnits() > 1) then
						pNewUnit:JumpToNearestValidPlot()
					else
						pNewUnit:SetExperience(0)
					end
  					return
				end
			end
		end

	end
end
GameEvents.TeamTechResearched.Add(AmonResearchComplete)
 
Not sure if the code came out any shorter, but it is more adaptive to what is in the game database for table units and therefore auto-accomodative to mods such as Enlightenment Era and Future Worlds, and it is more adaptive to the correct unique units within a class without need for extra code beyond that already written. There are a couple of limitations explained within the notes in the spoiler.
Spoiler :
Code:
code removed from here (see post #18 downthread)
  1. As written, each technology will only spawn one type of unit, and it will be the unit that has the lowest ID# of all those unlocked by the same technology.
    • It is possible to address this issue (such as with Tech Civil Service) but at the moment I couldn't see a method to get there without my head wanting to explode.
  2. Resource requirement issues are NOT addressed by the code
  3. With the way domains are excluded in the code, only Land and Sea units will be spawned. Sea units will only be spawned if the player has a coastal city.
    • Trust me, you do not want to have to try to account for all the issues that arise from units that are immobile, units that are Air units, and other unfriendly what-not associated with spawning units that are not Land or Sea units.
  4. If more than one player within a game is being used as your civilization, all these civs will be handled.
 
Okay, so I implemented the code, but I can't seem to get anything. I'm not really sure I've implemented the following code correctly:

Code:
local tTechsToFreeUnits = {["TECH_ARCHERY"]="included", ["TECH_SAILING"]="included", ["TECH_THE_WHEEL"]="included", ["TECH_BRONZE_WORKING"]="included", ["TECH_HORSEBACK_RIDING"]="included", ["TECH_MATHEMATICS"]="included", ["TECH_CONSTRUCTION"]="included", ["TECH_IRON_WORKING"]="included", ["TECH_CIVIL_SERVICE"]="included", ["TECH_COMPASS"]="included", ["TECH_CHIVALRY"]="included", ["TECH_MACHINERY"]="included", ["TECH_PHYSICS"]="included", ["TECH_STEEL"]="included", ["TECH_ASTRONOMY"]="included", ["TECH_GUNPOWDER"]="included", ["TECH_NAVIGATION"]="included", ["TECH_METALLURGY"]="included", ["TECH_CHEMISTRY"]="included", ["TECH_INDUSTRIALIZATION"]="included", ["TECH_RIFLING"]="included", ["TECH_MILITARY_SCIENCE"]="included", ["TECH_STEAM_POWER"]="included", ["TECH_DYNAMITE"]="included", ["TECH_REFRIGERATION"]="included", ["TECH_REPLACEABLE_PARTS"]="included", ["TECH_PLASTICS"]="included", ["TECH_ELECTRONICS"]="included", ["TECH_BALLISTICS"]="included", ["TECH_COMBUSTION"]="included", ["TECH_PENICILLIN"]="included", ["TECH_COMBINED_ARMS"]="included", ["TECH_NUCLEAR_FISSION"]="included", ["TECH_ROCKETRY"]="included", ["TECH_TELECOM"]="included", ["TECH_MOBILE_TACTICS"]="included", ["TECH_ROBOTICS"]="included", ["TECH_LASERS"]="included", ["TECH_NUCLEAR_FUSION"]="included", ["TECH_NANOTECHNOLOGY"]="included"}
I am positive this is the only thing wrong with it, as it's the only thing I've touched myself.
 
Okay, so I implemented the code, but I can't seem to get anything. I'm not really sure I've implemented the following code correctly:

Code:
local tTechsToFreeUnits = {["TECH_ARCHERY"]="included", ["TECH_SAILING"]="included", ["TECH_THE_WHEEL"]="included", ["TECH_BRONZE_WORKING"]="included", ["TECH_HORSEBACK_RIDING"]="included", ["TECH_MATHEMATICS"]="included", ["TECH_CONSTRUCTION"]="included", ["TECH_IRON_WORKING"]="included", ["TECH_CIVIL_SERVICE"]="included", ["TECH_COMPASS"]="included", ["TECH_CHIVALRY"]="included", ["TECH_MACHINERY"]="included", ["TECH_PHYSICS"]="included", ["TECH_STEEL"]="included", ["TECH_ASTRONOMY"]="included", ["TECH_GUNPOWDER"]="included", ["TECH_NAVIGATION"]="included", ["TECH_METALLURGY"]="included", ["TECH_CHEMISTRY"]="included", ["TECH_INDUSTRIALIZATION"]="included", ["TECH_RIFLING"]="included", ["TECH_MILITARY_SCIENCE"]="included", ["TECH_STEAM_POWER"]="included", ["TECH_DYNAMITE"]="included", ["TECH_REFRIGERATION"]="included", ["TECH_REPLACEABLE_PARTS"]="included", ["TECH_PLASTICS"]="included", ["TECH_ELECTRONICS"]="included", ["TECH_BALLISTICS"]="included", ["TECH_COMBUSTION"]="included", ["TECH_PENICILLIN"]="included", ["TECH_COMBINED_ARMS"]="included", ["TECH_NUCLEAR_FISSION"]="included", ["TECH_ROCKETRY"]="included", ["TECH_TELECOM"]="included", ["TECH_MOBILE_TACTICS"]="included", ["TECH_ROBOTICS"]="included", ["TECH_LASERS"]="included", ["TECH_NUCLEAR_FUSION"]="included", ["TECH_NANOTECHNOLOGY"]="included"}
I am positive this is the only thing wrong with it, as it's the only thing I've touched myself.
Do not attempt to alter the code I gave you in the way you are. In fact, only alter this part of the code to match for the civilzation you need and if you want to change the Qty of each unit-type given, or if you want to make the code issue debugging statements to the game's lua.log:
Code:
local sCivilizationRequired = "CIVILIZATION_AMERICA"
	--adjust for the correct desired civ, but you need the " marks around the name of the civ
local iQtyUnitsOnTechDiscovery = 2
local bPrintDebug = false
As I wrote the code it will will:
  1. look through the game's database for the <Units> table after all mods add their data into the game.
  2. grab the PreReq technologies of each unit that has a Combat value > 0 and which is not an Air, Hover, or Immobile "Domain" of unit.
  3. Stick this into the proper place in table tTechsToFreeUnits, along with the correct unit (default or unique) for your civ based on what is given here
    Code:
    local sCivilizationRequired = "CIVILIZATION_AMERICA"
    and will gather the other needed data for the code to run properly

  • The issues with Air units created directly by lua-code are:
    • there are far too many circumstances in which you get useless air units or air units that will be deleted from the game as soon as you progress to the next turn.
    • This has to do with stacking limits for Air units on the same tile, and potential inability to 'rebase' if you have exceeded the stacking limits as a result of getting 'free' Air units from an LUA method.
    • the only way you can progress to the next turn in these conditions is to put the units to sleep
    • once you put the units to sleep you cannot access them again without going into the Military Overview panel and finding and re-activating them there.
    • If you cannot rebase the units (because for example for Air Units there may be nowhere for them to go) then as soon as you press "Next Turn" the units are deleted from the game
    • There are related issues with Aircraft Carriers, too many units on the aircraft carrier, etc., etc.

  • The issue with only one type of unit per technology could be solved with a bit more thinking about the issues involved in scanning through the <Units> table, and a little adaptation of the code, but at the moment I don't really see myself having the time to look into it again until the weekend.
 
I only altered the code because when I used the code as it was, it didn't seem to do anything either. I would research Archery and receive no Archers, you see. So, I assumed I needed to add that in to fix the problem.

-EDIT-
After removing the changes I made and trying it again, I browsed through the .Lua in the Logs and found this:
Code:
[7120.618] Runtime Error: C:\...\Sid Meier's Civilization 5\MODS\Harkodos' The Equalists (BNW)\Lua/Amon_UA.lua:12: attempt to index global 'GameInfoType' (a nil value)
[7120.618] Runtime Error: Error loading C:\...\Sid Meier's Civilization 5\MODS\Harkodos' The Equalists (BNW)\Lua/Amon_UA.lua.
 
change this line
Code:
local iRequiredCivilization = GameInfoType[sCivilizationRequired]
to this
Code:
local iRequiredCivilization = GameInfoTypes[sCivilizationRequired]

I edited post #12 to fix the typo there as well.

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

If there are other errors getting posted in the lua.log after you fix that typo, let me know and I'll do a more-intense insect-hunt in the morning.
 
I made the changes and now I get units when I discover technologies, but I still only get 1 of each unit, rather than 2.

Didn't get any problems in the .Lua Log at least. So the problem must involve iQtyUnitsOnTechDiscovery in some way.
 
It was a variable localization error (which embarrasses me given the number of times I've pointed them out to other people). Corrected code in the spoiler. I verified using America and got 2 Archers, 2 Spearman, 2 Swordsman when I should have, and 2 Minutemen when I gave myself Gunpowder (as I should for America).
Spoiler :
Code:
local sCivilizationRequired = "CIVILIZATION_AMERICA"
	--adjust for the correct desired civ, but you need the " marks around the name of the civ
local iQtyUnitsOnTechDiscovery = 2
local bPrintDebug = false
local tExcludedUnitDomains = {["DOMAIN_AIR"]="excluded", ["DOMAIN_IMMOBILE"]="excluded", ["DOMAIN_HOVER"]="excluded"}
	--these unit domains are excluded because they are a PITA to deal with via lua
local tTechsToFreeUnits = { }
local iRequiredCivilization = GameInfoTypes[sCivilizationRequired]

function PrintMessagingToLog(sMessage)
	if bPrintDebug then
		print(sMessage)
	end
end
function GetUniqueByUnitClass(playerCivilizationName, sUnitClass)
	-- Set 1st to default unit ID
	PrintMessagingToLog("inside GetUniqueByUnitClass the playerCivilizationName is " .. playerCivilizationName)
	PrintMessagingToLog("inside GetUniqueByUnitClass the UnitClass is " .. sUnitClass)
	local sUnitName = "NONE"
	for row in GameInfo.UnitClasses("Type='" .. sUnitClass .. "'") do
		sUnitName = row.DefaultUnit
	end
	-- Get Unique Unit Name
	for row in GameInfo.Civilization_UnitClassOverrides("CivilizationType='" .. playerCivilizationName .. "'") do
		if sUnitClass == row.UnitClassType then
			sUnitName = row.UnitType
		end
	end
	PrintMessagingToLog("inside GetUniqueByUnitClass the sUnitName to be returned is " .. sUnitName)
	return sUnitName
end
for row in GameInfo.Units() do
	if (row.PrereqTech ~= "NULL") and (row.PrereqTech ~= nil) then
		local iPreReqTech = GameInfoTypes[row.PrereqTech]
		if not tTechsToFreeUnits[iPreReqTech] then
			if not tExcludedUnitDomains[row.Domain] and (row.Combat > 0) and (GetUniqueByUnitClass(sCivilizationRequired, row.Class) == row.Type) then
				tTechsToFreeUnits[iPreReqTech] = {Unit=row.ID, UnitType=row.Type, Domain=row.Domain, Description=Locale.ConvertTextKey(row.Description), TechnologyDescription=Locale.ConvertTextKey(GameInfo.Technologies[iPreReqTech].Description) , Qty=iQtyUnitsOnTechDiscovery}
			end
		end
	end
end
function AmonResearchComplete(iTeam, iTech, iChange)
	if tTechsToFreeUnits[iTech] then
		for iPlayerID = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
			local pScanPlayer = Players[iPlayerID]
			if pScanPlayer:IsAlive() and pScanPlayer:GetCivilizationType() == iRequiredCivilization then
				if pScanPlayer:GetTeam() == iTeam then
					local pCapital = pScanPlayer:GetCapitalCity()
					local bSpawnUnits = (pCapital ~= nil)
					if bSpawnUnits and (tTechsToFreeUnits[iTech].Domain == "DOMAIN_SEA") then
						if not pCapital:IsCoastal(10) then
							bSpawnUnits = false
							pCapital = nil
							for pCity in pScanPlayer:Cities() do
								if pCity:IsCoastal(10) then
									pCapital = pCity
									bSpawnUnits = true
									break
								end
							end
						end
						if (pCapital == nil) or (bSpawnUnits == false) then
							PrintMessagingToLog("Cannot Spawn any " .. tTechsToFreeUnits[iTech].Description .. " units on discovery of technology " .. tTechsToFreeUnits[iTech].TechnologyDescription .. " for player " .. pScanPlayer:GetName() .. " because " .. pScanPlayer:GetName() .. " has no qualifying coastal cities")
							bSpawnUnits = false	--just extra insurance we aren't going to spawn landlocked sea units if we manage to get here
						end
					end
					if bSpawnUnits then
						local iQty = tTechsToFreeUnits[iTech].Qty
						while iQty > 0 do
							local pNewUnit = pScanPlayer:InitUnit(tTechsToFreeUnits[iTech].Unit, pCapital:GetX(), pCapital:GetY())
							if (pNewUnit and pCapital:Plot():GetNumUnits() > 1) then
								pNewUnit:JumpToNearestValidPlot()
							else
								pNewUnit:SetExperience(0)
							end
							iQty = iQty - 1
						end
					end
  				end
			end
		end
	end
end
GameEvents.TeamTechResearched.Add(AmonResearchComplete)

print("Code for AmonResearchComplete loaded to the end without fatal syntax errors")
I also added a print confirmation line at the end that the code loaded all the way to the end without fatal syntax errors. This will make the line
Code for AmonResearchComplete loaded to the end without fatal syntax errors
appear in the lua.log whenever the mod loads into the game as a normal confirmation that everything loaded properly and no new fatal errors have been introduced into the code by edits and what-not.

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

I'll try to look into the issue related to the way the code is currently written only gives one unit-type from each tech sometime over the next few days.
 
Back
Top Bottom