Some help and question for Lua

Ah, thanks for the explanation and shared thread! (turns out I "only" forgot to add the squared brackets and kept wondering why it didn't have the correct syntax >.<)
Though I'm still confused about one thing:
Why does this work (in this specific case):
Code:
local tValidFasilGhebbiUnitDomains = { [GameInfoTypes.DOMAIN_LAND] = GameInfoTypes.DOMAIN_LAND, [GameInfoTypes.DOMAIN_SEA] = GameInfoTypes.DOMAIN_SEA }

but this doesn't?
Code:
iUnitDomains = { [GameInfoTypes.DOMAIN_LAND] = true, [GameInfoTypes.DOMAIN_SEA] = true }

EDIT: or does it both work and am I just misunderstanding you? :/ If so, why did you choose to define it like the first block of code in this post?
 
Ah, thanks for the explanation and shared thread! (turns out I "only" forgot to add the squared brackets and kept wondering why it didn't have the correct syntax >.<)
Though I'm still confused about one thing:
Why does this work (in this specific case):
Code:
local tValidFasilGhebbiUnitDomains = { [GameInfoTypes.DOMAIN_LAND] = GameInfoTypes.DOMAIN_LAND, [GameInfoTypes.DOMAIN_SEA] = GameInfoTypes.DOMAIN_SEA }

but this doesn't?
Code:
iUnitDomains = { [GameInfoTypes.DOMAIN_LAND] = true, [GameInfoTypes.DOMAIN_SEA] = true }

EDIT: or does it both work and am I just misunderstanding you? :/ If so, why did you choose to define it like the first block of code in this post?

I can just as easily define the table like this:
  • Any of the three methods will work because as I am using the data in the table I am only checking whether the domain of the unit (GameInfoTypes.DOMAIN_LAND, etc.) is a 'key' within the specified table.
    • This allows me to not have to write
      Code:
      if pUnit:GetDomainType() == GameInfoTypes.DOMAIN_LAND or pUnit:GetDomainType() == GameInfoTypes.DOMAIN_SEA then
      And then have to come back and change the line with more (or less) conditions to be met if I later decide to change which domains the code should apply for. With data-driven techniques I only have to alter the definition of the table and I never have to add or remove or alter any conditional-check lines with the 'meat' of my code​
  • I am not concerned as my code is written with what is in any of the 'value' sides of a 'key,value' pair within the table tValidFasilGhebbiUnitDomains.
  • I wrote the table to use "GameInfoTypes.DOMAIN_LAND" on the 'value' side of the 'key,value' pair in case I needed that info at any point in the code. As it turned out, in the Fasil Ghebbi code I did not need to use the 'value' side of any pair within the table, which is why I said I could just as easily have defined the 'value' side of the pairs as "true" or "Hamburgers" -- I just need something to 'placehold' the "value" side of the lua-table 'key,value' pair to ensure for my own paranoia that lua does not see the table definition as invalid or as something it can delete from the code because of Garbage Collection Rules in lua.
  • My paranoia for such things is why I also tend to give variables values of "NONE" rather than let them remain 'nil' if not otherwise written-to during the execution of an lua-script. And also why I tend not to give them values of "-1" when I "initialize" a variable. This habit is at wide variance with how just about anyone else writes lua-script, but my personal programming experience history comes from really idiosyncratic programming systems where defining a variable as local ithing without an assignment of a value either was not allowed or was a really really really bad thing.
 
Thanks for clarifying that! :D


Sorry for hijacking your thread OTiger
 
No problem! That's two down, one more to go, maybe...

Anyways, is there somewhere to transfer promotions with some Lua hook?
Code:
pFirstUnit:SetHasPromotion(iPromotion,false)
pOtherUnit:SetHasPromotion(iPromotion,true)
This simply removes the promotion from one unit and gives* it to another unit.

*The promotion is not transferred from pFirstUnit to pOtherUnit! We simply give pOtherUnit the promotion "out of nowhere" like we did in the decepticon and autobot codes.


And how could you try to a promotion that there can be only one unit with that promotion at a time?
Simply don't give any other units the same promotion as well. (Store a value in a boolean variable if the promotion is already on a unit, and only grant the promotion if that boolean is false)

E.g:
Code:
local iOurCiv = GameInfoTypes.CIVILIZATION_OTIGER
local iSpecialPromotion = GameInfoTypes.PROMOTION_ONLY_ONE
local bPromotionActive = false --there would be issues when reloading the game, since this variable will be reset (even if there's already a unit with the promotion!)

function GrantSpecialPromotion(pUnit)
     if not bPromotionActive then
           bPromotionActive = true
           pUnit:SetHasPromotion(iSpecialPromotion,true)     
     end

end
To solve that issue we should loop through every player, and if that player is our specific civ, loop through all of their units. (A variant of what LeeS did to the Autobot-finished policy branches code):

Add this instead of bPromotionActive = false:
Code:
bPromotionActive = false
for iPlayer = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
		local pPlayer = Players[iPlayer]
		if pPlayer ~= nil then
			if pPlayer:GetCivilizationType() == iOurCiv then
				for pUnit in pPlayer:Units() do
					if pUnit:IsHasPromotion(iSpecialPromotion) then
                                              bPromotionActive=true
                                        end
				end
			end
		end
end
NOTE: We could also pack that in a function if we would ever wish to re-check it later

So now, whenever you might want to give a unit the promotion, we could simply call our GrantSpecialPromotion-function and it does all our check-ups for us. (NOTE: It was not specifically necessary to pack our check-ups in a special function, but if you would want to do this check-up multiple times in the code (E.g. when you want to give the promotion if a unit captures a city or whenever it is upgraded), this saves space
 
I tried to combine the codes from you have, Troller. I also used the OnUnitKilledInCombat too. This code is for a Unique Unit, BTW.
Since it'll use Unit_FreePromotions for the Unique Promotion in the XML file, would it still work?


Code:
local iMatrix = GameInfoTypes.PROMOTION_MATRIX_BEARER;
local iCiv = GameInfoTypes.CIVILIZATION_AUTOBOTS
local iMatrix = GameInfoTypes.PROMOTION_MATRIX_BEARER

-- This function transfers the promotion upon death.
local function MatrixTranferFromDeath(iKiller, iOwner, iUnitType)
	if GetUnit(unitID):IsHasPromotion(iMatrix) then
		pFirstUnit:SetHasPromotion(iPromotion,false)
		pOtherUnit:SetHasPromotion(iPromotion,true)
	end
end
GameEvents.MatrixTranferFromDeath.Add(OnUnitKilledInCombat)

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

-- This code here makes special promotion to be active one unit at a time.
local bPromotionActive = false 

function GrantSpecialPromotion(pUnit)
     if not bPromotionActive then
           bPromotionActive = false
           pUnit:SetHasPromotion(iSpecialPromotion,true)     
     end

end

bPromotionActive = false
for iPlayer = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
		local pPlayer = Players[iPlayer]
		if pPlayer ~= nil then
			if pPlayer:GetCivilizationType() == iCiv then
				for pUnit in pPlayer:Units() do
					if pUnit:IsHasPromotion(iSpecialPromotion) then
                                              bPromotionActive=true
                                        end
				end
			end
		end
end
 
You don't have pFirstUnit and pOtherUnit defined anywhere, and
Code:
GameEvents.MatrixTranferFromDeath.Add(OnUnitKilledInCombat)
should be
Code:
GameEvents.UnitKilledInCombat.Add(MatrixTranferFromDeath)
More importantly, you'll need a function tied to UnitTrained, SerialEventUnitCreated, or similar that removes the promotion if bPromotionActive is true.
 
[...]
Since it'll use Unit_FreePromotions for the Unique Promotion in the XML file, would it still work?
[...]

No, that won't work since the XML doesn't care about our 'special handling' of the promotion. It'll simply always give the unit the promotion.

You would always have to manually give the promotion to any unit via our GrantSpecialPromotion-method in order to achieve it what you want.
What you could do is execute the special handler whenever a unit is trained (or bought) within a city. So like this:
Code:
iOurCiv = GameInfoTypes.CIVILIZATION_AUTOBOTS
iOurUnit = GameInfoTypes.UNIT_BUMBLE_BEE

function OnUUTrained(iPlayer,iCity,iUnit,bGold,bFaithOrCulture)
     local pPlayer = Players[iPlayer]
     local pUnit = pPlayer:GetUnitByID(iUnit) 
     local pCity = pPlayer:GetCityByID(iCity)    

     if pPlayer:GetCivilizationType() == iOurCiv then
          if pUnit:GetUnitType() == iOurUnit then
               GrantSpecialPromotion(pUnit)
               print("Our Unit was trained in "..pCity:GetName().."; The special Promotion-handler will now execute)
          end
     end
end

GameEvents.CityTrained.Add(OnUUTrained)

EDIT: Or you could do what Klisz suggested in his last sentence/line

EDIT2: I noticed I made a typo in post #25:
Code:
[..]
function GrantSpecialPromotion(pUnit)
     if not bPromotionActive then
           bPromotionActive = [S][COLOR="DarkRed"]false[/COLOR][/S] [COLOR="DarkGreen"]true[/COLOR]
[..]
 
OK, here's the code I wrote with you all of your help. Although I'll need more help:
1.) This UU here is an Great General Replacement. Would the OnUUTrained function need to be changed?
2.) I'm having trouble trying to define pOtherUnit. Any other military unit would get the promotion once the other unit with the promotion dies. Is it possible to have GetUnit(unitID) to define all other military units?

Code:
local iOurCiv = GameInfoTypes.CIVILIZATION_AUTOBOTS
local iOurUnit = GameInfoTypes.UNIT_PRIME

function OnUUTrained(iPlayer,iCity,iUnit,bGold,bFaithOrCulture)
     local pPlayer = Players[iPlayer]
     local pUnit = pPlayer:GetUnitByID(iUnit) 
     local pCity = pPlayer:GetCityByID(iCity)    

     if pPlayer:GetCivilizationType() == iOurCiv then
          if pUnit:GetUnitType() == iOurUnit then
               GrantSpecialPromotion(pUnit)
               print("A Prime has been born in"..pCity:GetName().."; The special Promotion-handler will now execute")
          end
     end
end

GameEvents.CityTrained.Add(OnUUTrained)

-- This function transfers the promotion upon death.
local pFirstUnit = GetUnit(unitID):IsHasPromotion(iMatrix)
local pOtherUnit = GetUnit(unitID)

local function MatrixTranferFromDeath(iKiller, iOwner, iUnitType)
	if GetUnit(unitID):IsHasPromotion(iMatrix) then
		pFirstUnit:SetHasPromotion(iPromotion,false)
		pOtherUnit:SetHasPromotion(iPromotion,true)
	end
end
GameEvents.UnitKilledInCombat.Add(MatrixTranferFromDeath)



-- These function do something that I'll describe later.
local iMatrix = GameInfoTypes.PROMOTION_WHATEVER
local bPromotionActive = false --there would be issues when reloading the game, since this var
function GrantSpecialPromotion(pUnit)
     if not bPromotionActive then
           bPromotionActive = true
           pUnit:SetHasPromotion(iMatrix,true)     
     end

bPromotionActive = false
for iPlayer = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
		local pPlayer = Players[iPlayer]
		if pPlayer ~= nil then
			if pPlayer:GetCivilizationType() == iOurCiv then
				for pUnit in pPlayer:Units() do
					if pUnit:IsHasPromotion(iMatrix) then
                                              bPromotionActive=true
                                        end
				end
			end
		end
end
 
Assuming you always want one great general unit to have the promotion and you only want your great general unit(s) to be able to have the promotion, I think this code will work:
Spoiler :
Code:
local iOurCiv = GameInfoTypes.CIVILIZATION_AUTOBOTS
local iOurUnit = GameInfoTypes.UNIT_PRIME
local iMatrix = GameInfoTypes.PROMOTION_WHATEVER
local bPromotionActive = false
local bOurCivIsActive = false

function DoesAnyUnitHaveTheSpecialPromotion(pPlayer, iPromotion)
	for pUnit in pPlayer:Units() do
		if pUnit:IsHasPromotion(iPromotion) then
			return true		
		end
	end
	return false
end
function AutoBotUnitDetect(PlayerID, UnitID, hexVec, unitType, cultureType, civID, primaryColor, secondaryColor, unitFlagIndex, fogState)
	--print("function AutoBotUnitDetect was executed")
	local pPlayer = Players[PlayerID]
	local pUnit = pPlayer:GetUnitByID(UnitID)
	if(pPlayer == nil or
		pUnit == nil or
		pUnit:IsDead()) then
		return
	end
	if pPlayer:GetCivilizationType() ~= iOurCiv then return end
	--check units for the special promotion
	if not DoesAnyUnitHaveTheSpecialPromotion(pPlayer, iMatrix) and (pUnit:GetUnitType() == iOurUnit) then
		pUnit:SetHasPromotion(iMatrix,true)
		bPromotionActive = true
	end
end
function AutoBotUnitKilled(iOwner, iUnit, iUnitType, iX, iY, bDelay, iKiller)
	local pPlayer = Players[iOwner]
	if pPlayer:GetCivilizationType() ~= iOurCiv then return end
	if not bDelay then return end
	if iUnitType == iOurUnit then
		if bPromotionActive and not DoesAnyUnitHaveTheSpecialPromotion(pPlayer, iMatrix) then
			bPromotionActive = false --it WAS active but in order to get here no unit can CURRENTLY have the promotion
			for pUnit in pPlayer:Units() do
				if (pUnit:GetUnitType() == iOurUnit) then
					pUnit:SetHasPromotion(iMatrix,true)
					bPromotionActive = true
					return		
				end
			end
		end
	end
end


for iPlayer = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
	local pPlayer = Players[iPlayer]
	if pPlayer ~= nil then
		if pPlayer:GetCivilizationType() == iOurCiv then
			bPromotionActive = DoesAnyUnitHaveTheSpecialPromotion(pPlayer, iMatrix)
			bOurCivIsActive = true
		end
	end
end
function AutoBotsLoadScreenClose()
	print("function AutoBotsLoadScreenClose was executed: SerialEventUnitCreated function AutoBotUnitDetect was activated")
	Events.SerialEventUnitCreated.Add(AutoBotUnitDetect) 
end
if bOurCivIsActive then
	Events.LoadScreenClose.Add(AutoBotsLoadScreenClose)
	GameEvents.UnitPrekill.Add(AutoBotUnitKilled)
end
 
Assuming you always want one great general unit to have the promotion and you only want your great general unit(s) to be able to have the promotion, I think this code will work:
Spoiler :
Code:
local iOurCiv = GameInfoTypes.CIVILIZATION_AUTOBOTS
local iOurUnit = GameInfoTypes.UNIT_PRIME
local iMatrix = GameInfoTypes.PROMOTION_WHATEVER
local bPromotionActive = false
local bOurCivIsActive = false

function DoesAnyUnitHaveTheSpecialPromotion(pPlayer, iPromotion)
	for pUnit in pPlayer:Units() do
		if pUnit:IsHasPromotion(iPromotion) then
			return true		
		end
	end
	return false
end
function AutoBotUnitDetect(PlayerID, UnitID, hexVec, unitType, cultureType, civID, primaryColor, secondaryColor, unitFlagIndex, fogState)
	--print("function AutoBotUnitDetect was executed")
	local pPlayer = Players[PlayerID]
	local pUnit = pPlayer:GetUnitByID(UnitID)
	if(pPlayer == nil or
		pUnit == nil or
		pUnit:IsDead()) then
		return
	end
	if pPlayer:GetCivilizationType() ~= iOurCiv then return end
	--check units for the special promotion
	if not DoesAnyUnitHaveTheSpecialPromotion(pPlayer, iMatrix) and (pUnit:GetUnitType() == iOurUnit) then
		pUnit:SetHasPromotion(iMatrix,true)
		bPromotionActive = true
	end
end
function AutoBotUnitKilled(iOwner, iUnit, iUnitType, iX, iY, bDelay, iKiller)
	local pPlayer = Players[iOwner]
	if pPlayer:GetCivilizationType() ~= iOurCiv then return end
	if not bDelay then return end
	if iUnitType == iOurUnit then
		if bPromotionActive and not DoesAnyUnitHaveTheSpecialPromotion(pPlayer, iMatrix) then
			bPromotionActive = false --it WAS active but in order to get here no unit can CURRENTLY have the promotion
			for pUnit in pPlayer:Units() do
				if (pUnit:GetUnitType() == iOurUnit) then
					pUnit:SetHasPromotion(iMatrix,true)
					bPromotionActive = true
					return		
				end
			end
		end
	end
end


for iPlayer = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
	local pPlayer = Players[iPlayer]
	if pPlayer ~= nil then
		if pPlayer:GetCivilizationType() == iOurCiv then
			bPromotionActive = DoesAnyUnitHaveTheSpecialPromotion(pPlayer, iMatrix)
			bOurCivIsActive = true
		end
	end
end
function AutoBotsLoadScreenClose()
	print("function AutoBotsLoadScreenClose was executed: SerialEventUnitCreated function AutoBotUnitDetect was activated")
	Events.SerialEventUnitCreated.Add(AutoBotUnitDetect) 
end
if bOurCivIsActive then
	Events.LoadScreenClose.Add(AutoBotsLoadScreenClose)
	GameEvents.UnitPrekill.Add(AutoBotUnitKilled)
end

The code works, but it doesn't seem to transfer the promotion upon death. Do you know what's going wrong?
I have the Lua.log file here.
I swapped the Autobots to the Americans and the Prime unit to a Warrior unit in order to test the code out.

(There's also an error I'm getting for another older mod from the Lua code that you made. Is it alright for you to check out that error, too?
Here's the Lua if you're curious:
Spoiler :
Code:
--+2 Culture and +1 happiness per CS Ally and +3% per CS ally during Golden Ages and lowered CS degradtion during Golden Ages
gSpongeDummy = GameInfoTypes.BUILDING_SPONGEBOBDUMMY
local iGoldenAgeDummy = GameInfoTypes.BUILDING_SPONGEBOBDUMMY2
local iNeededPlayer = GameInfoTypes.CIVILIZATION_BIKINI_BOTTOM
local policySpongebobUA = GameInfoTypes.POLICY_SPONGEBOBUA
local policySpongebobPlaceholder = GameInfoTypes.POLICY_SPONGEBOB_PLACEHOLDER
function SpongebobUA(iPlayer)
	local pPlayer = Players[iPlayer]
	if pPlayer:GetCivilizationType() == iNeededPlayer then
		local iCultureHappinessBonus = 0
		local pCapitalCity = pPlayer:GetCapitalCity()
		for iLoopPlayer, pLoopPlayer in pairs(Players) do
			if pLoopPlayer:IsMinorCiv() then
				if pLoopPlayer:GetMinorCivFriendshipLevelWithMajor(iPlayer) >= 2 then
					iCultureHappinessBonus = iCultureHappinessBonus + 1
				end
			end
		end
		pCapitalCity:SetNumRealBuilding(gSpongeDummy , iCultureHappinessBonus)
		print("Setting " .. iCultureHappinessBonus .. " Spongebob Dummy Buildings into the capital city of " .. pCapitalCity:GetName())
		if pPlayer:IsGoldenAge() then
			pCapitalCity:SetNumRealBuilding(iGoldenAgeDummy , iCultureHappinessBonus)
			if not pPlayer:HasPolicy(policySpongebobUA) and not pPlayer:HasPolicy(policySpongebobPlaceholder) then
				pPlayer:SetNumFreePolicies(1)
				pPlayer:SetNumFreePolicies(0)
				pPlayer:SetHasPolicy(policySpongebobUA, true)
			elseif pPlayer:HasPolicy(policySpongebobPlaceholder) then
				pPlayer:SetHasPolicy(policySpongebobPlaceholder, false)
				pPlayer:SetHasPolicy(policySpongebobUA, true)
			end
		else
			pCapitalCity:SetNumRealBuilding(iGoldenAgeDummy , 0)
			if pPlayer:HasPolicy(policySpongebobUA) then
				pPlayer:SetHasPolicy(policySpongebobUA, false)
				pPlayer:SetHasPolicy(policySpongebobPlaceholder, true)
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(SpongebobUA)

And the error itself)
Spoiler :
[6324.937] Runtime Error: C:\Users\Admin\Documents\My Games\Sid Meier's Civilization 5\MODS\Spongebob Squarepants Spongebob and Bikini Bottom (BNW Only) (v 1)\Lua\SpongebobUA.lua:24: attempt to index local 'pCapitalCity' (a nil value)
stack traceback:
C:\Users\Admin\Documents\My Games\Sid Meier's Civilization 5\MODS\Spongebob Squarepants Spongebob and Bikini Bottom (BNW Only) (v 1)\Lua\SpongebobUA.lua:24: in function <C:\Users\Admin\Documents\My Games\Sid Meier's Civilization 5\MODS\Spongebob Squarepants Spongebob and Bikini Bottom (BNW Only) (v 1)\Lua\SpongebobUA.lua:12>
 

Attachments

The runtime error is occuring apparently on the 1st turn, which it would if you move your settler rather than founding right away. The error in such a case is because there's no capital city yet. Note that the print-out
SpongebobUA: Setting 0 Spongebob Dummy Buildings into the capital city of Bikini Bottom
Occurs multiple times in your log, and is generated by the same code. You will get '0' buildings when you have '0' city-state friendships.
Change the Spongebob code to
Code:
--+2 Culture and +1 happiness per CS Ally and +3% per CS ally during Golden Ages and lowered CS degradtion during Golden Ages
gSpongeDummy = GameInfoTypes.BUILDING_SPONGEBOBDUMMY
local iGoldenAgeDummy = GameInfoTypes.BUILDING_SPONGEBOBDUMMY2
local iNeededPlayer = GameInfoTypes.CIVILIZATION_BIKINI_BOTTOM
local policySpongebobUA = GameInfoTypes.POLICY_SPONGEBOBUA
local policySpongebobPlaceholder = GameInfoTypes.POLICY_SPONGEBOB_PLACEHOLDER
function SpongebobUA(iPlayer)
	local pPlayer = Players[iPlayer]
	if pPlayer:GetCivilizationType() == iNeededPlayer then
		local iCultureHappinessBonus = 0
		local pCapitalCity = pPlayer:GetCapitalCity()
		for iLoopPlayer, pLoopPlayer in pairs(Players) do
			if pLoopPlayer:IsMinorCiv() then
				if pLoopPlayer:GetMinorCivFriendshipLevelWithMajor(iPlayer) >= 2 then
					iCultureHappinessBonus = iCultureHappinessBonus + 1
				end
			end
		end
		if pCapitalCity ~= nil then
			pCapitalCity:SetNumRealBuilding(gSpongeDummy , iCultureHappinessBonus)
			print("Setting " .. iCultureHappinessBonus .. " Spongebob Dummy Buildings into the capital city of " .. pCapitalCity:GetName())
		else
			print("pCapitalCity was nil so there was nothing to do")
		end
		if pPlayer:IsGoldenAge() then
			if pCapitalCity ~= nil then
				pCapitalCity:SetNumRealBuilding(iGoldenAgeDummy , iCultureHappinessBonus)
			else
				print("pCapitalCity was nil so there was nothing to do for the golden age dummy building")
			end
			if not pPlayer:HasPolicy(policySpongebobUA) and not pPlayer:HasPolicy(policySpongebobPlaceholder) then
				pPlayer:SetNumFreePolicies(1)
				pPlayer:SetNumFreePolicies(0)
				pPlayer:SetHasPolicy(policySpongebobUA, true)
			elseif pPlayer:HasPolicy(policySpongebobPlaceholder) then
				pPlayer:SetHasPolicy(policySpongebobPlaceholder, false)
				pPlayer:SetHasPolicy(policySpongebobUA, true)
			end
		else
			if pCapitalCity ~= nil then
				pCapitalCity:SetNumRealBuilding(iGoldenAgeDummy , 0)
			else
				print("pCapitalCity was nil so there was nothing to do for the golden age dummy building")
			end
			if pPlayer:HasPolicy(policySpongebobUA) then
				pPlayer:SetHasPolicy(policySpongebobUA, false)
				pPlayer:SetHasPolicy(policySpongebobPlaceholder, true)
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(SpongebobUA)
This will avoid the runtime errors in the lua log and will instead force the linked print statement to that condition, but the end effect will be the same

On the promotion thing I'll test it a little later and get back to you.
 
Apparantly the game is not liking the manipulations I was trying to do during UnitPreKill. This is odd because I could have sworn I was able to get the game to do similar stuff as part of the Scipio mod. The following code is tested and working with the "stand-in" values as set at the top.
  1. When a unit with the special promotion is removed from play, on the next turn processing the special promotion will be given to another unit the player has of the same unit-type unless
  2. A unit of the same player makes a move after the removed unit was removed and before the NEXT TURN is pressed. In which case this 'moving' unit will cause the promotion to be applied to an appropriate unit.
  3. If the player has no more of the correct type of unit, then the promotion is not applied to any unit under either condition #1 or #2, and the player must wait until the next correct unit of the unit-type is created.
  4. If a player loses or disbands their unit with the special promotion, then saves game, then exits, they will not get the promotion transferred after they reload the save game, even if they own a qualifying unit at that time. This would require data persistence of one method or another merely to do the promotion transfer thing.

Spoiler :
Code:
local iOurCiv = GameInfoTypes.CIVILIZATION_AMERICA
local iOurUnit = GameInfoTypes.UNIT_WARRIOR
local iMatrix = GameInfoTypes.PROMOTION_HEROISM
local bPromotionActive = false
local bOurCivIsActive = false
local bPrintDebug = false

function DoesAnyUnitHaveTheSpecialPromotion(pPlayer, iPromotion, pExcludeUnit)
	for pUnit in pPlayer:Units() do
		if pUnit:IsHasPromotion(iPromotion) then
			if pExcludeUnit ~= nil then
				if pExcludeUnit ~= pUnit then
					return true
				end
			end		
		end
	end
	return false
end
function DebugMode(sMessage)
	if bPrintDebug then
		print(sMessage)
	end
end
function AutoBotUnitDetect(PlayerID, UnitID, hexVec, unitType, cultureType, civID, primaryColor, secondaryColor, unitFlagIndex, fogState)
	DebugMode("function AutoBotUnitDetect was executed")
	local pPlayer = Players[PlayerID]
	local pUnit = pPlayer:GetUnitByID(UnitID)
	if(pPlayer == nil or
		pUnit == nil or
		pUnit:IsDead()) then
		return
	end
	if pPlayer:GetCivilizationType() ~= iOurCiv then return end
	--check units for the special promotion
	if not bPromotionActive then
		if pUnit:GetUnitType() == iOurUnit then
			if not DoesAnyUnitHaveTheSpecialPromotion(pPlayer, iMatrix) then
				pUnit:SetHasPromotion(iMatrix,true)
				bPromotionActive = true
			end
		end
	end
end
function XYFixAutoBotSpecialPromotions(playerID, unitID, unitX, unitY)
	local pPlayer = Players[playerID]
	if pPlayer:GetCivilizationType() == iOurCiv then
		if bPromotionActive and not DoesAnyUnitHaveTheSpecialPromotion(pPlayer, iMatrix) then
			for pUnit in pPlayer:Units() do
				if pUnit:GetUnitType() == iOurUnit then
					pUnit:SetHasPromotion(iMatrix,true)
					return
				end
			end
			bPromotionActive = false
		end
	end
end
function TurnFixAutoBotSpecialPromotions(playerID)
	local pPlayer = Players[playerID]
	if pPlayer:GetCivilizationType() == iOurCiv then
		if bPromotionActive and not DoesAnyUnitHaveTheSpecialPromotion(pPlayer, iMatrix) then
			for pUnit in pPlayer:Units() do
				if pUnit:GetUnitType() == iOurUnit then
					pUnit:SetHasPromotion(iMatrix,true)
					return
				end
			end
			bPromotionActive = false
		end
	end
end
for iPlayer = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
	local pPlayer = Players[iPlayer]
	if pPlayer ~= nil then
		if pPlayer:GetCivilizationType() == iOurCiv then
			bPromotionActive = DoesAnyUnitHaveTheSpecialPromotion(pPlayer, iMatrix)
			bOurCivIsActive = true
		end
	end
end
function AutoBotsLoadScreenClose()
	print("function AutoBotsLoadScreenClose was executed: Required Game Events were activated")
	Events.SerialEventUnitCreated.Add(AutoBotUnitDetect) 
	GameEvents.UnitSetXY.Add(XYFixAutoBotSpecialPromotions)
	GameEvents.PlayerDoTurn.Add(TurnFixAutoBotSpecialPromotions)
end
if bOurCivIsActive then
	Events.LoadScreenClose.Add(AutoBotsLoadScreenClose)
end
 
Apparantly the game is not liking the manipulations I was trying to do during UnitPreKill. This is odd because I could have sworn I was able to get the game to do similar stuff as part of the Scipio mod. The following code is tested and working with the "stand-in" values as set at the top.
  1. When a unit with the special promotion is removed from play, on the next turn processing the special promotion will be given to another unit the player has of the same unit-type unless
  2. A unit of the same player makes a move after the removed unit was removed and before the NEXT TURN is pressed. In which case this 'moving' unit will cause the promotion to be applied to an appropriate unit.
  3. If the player has no more of the correct type of unit, then the promotion is not applied to any unit under either condition #1 or #2, and the player must wait until the next correct unit of the unit-type is created.
  4. If a player loses or disbands their unit with the special promotion, then saves game, then exits, they will not get the promotion transferred after they reload the save game, even if they own a qualifying unit at that time. This would require data persistence of one method or another merely to do the promotion transfer thing.

Cool, it works! BTW, what changes would I need to make if I had to make the promotion transferring work for all land military units as well? I'm still debating If I should go through that.

Anyways, that's pretty much it now. And thanks for all of your help. There's one more code to write, but I'll try to write it by myself for now and I'll let you know if it doesn't work. (But first, the code is about a Unique Building where it gives out extra production in a conquered city. Do I use the same code from the UA for the Decepticons?) that And If there's any errors that I find with these codes, I post it here to let you guys know.
 
OK, I've written up this code for the UB I was talking about, borrowing code from other Civs.
Is this correct?

Code:
local iSmeltingPit = GameInfoTypes.BUILDING_SMELTING_PIT
local iSmeltDummy = GameInfoTypes.BUILDING_SMELTING_DUMMY
local pCity = plot:GetPlotCity();
local iNewOwner = cCity:GetOwner();
local iOldOwner = cCity:GetOriginalOwner();
local iPreviousOwner = cCity:GetPreviousOwner();
local iCiv = GameInfoTypes.CIVILIZATION_DECEPTICON

function SmeltingPitExtraProduction
	if pPlayer:GetCivilizationType() == iCiv then
		if iNewOwner ~= iOldOwner then 
			if (pCity:GetNumBuilding(iSmeltingPit) > 0) then
			print("This city has a Smelting Pit and it's been captured by the Decepticons!")
				pCity:SetNumRealBuilding (iSmeltDummy)
				print("A Smetling Pit Dummy Building has been added!")
			end
		end
	end

And anyways, I was wondering the question on my last post could be answered, (The promotion transferring to land military units as well) if that's possible.
 
If you mean this:
BTW, what changes would I need to make if I had to make the promotion transferring work for all land military units?
I'm not sure I'd have to think through the code to see how easy/pita it would be. Part of the issue is that if it is set only to transfer from Unit-X of Type-X to Unit-Y of Type-X then we know there are no issues with the promotion having odd or undesirable effects from being placed onto a unit. If the promotion can go form Unit-X of Type-X to Unit-Y of Type-Y, then there might be problems.

As an example, give this promotion to a Gunpowder unit and then see what happens (and moreso when that Gunpowder unit has either of the Blitz or Woodsman promotions):
Code:
<Row>
	<Type>PROMOTION_WALLACE_FIRST_STRIKE</Type>
	<Description>TXT_KEY_PROMOTION_WALLACE_FIRST_STRIKE</Description>
	<Help>TXT_KEY_PROMOTION_WALLACE_FIRST_STRIKE_HELP</Help>
	<Sound>AS2D_IF_LEVELUP</Sound>
	<PortraitIndex>59</PortraitIndex>
	<IconAtlas>ABILITY_ATLAS</IconAtlas>
	<PediaType>PEDIA_ATTRIBUTES</PediaType>
	<PediaEntry>TXT_KEY_PROMOTION_WALLACE_FIRST_STRIKE</PediaEntry>
	<RangedSupportFire>true</RangedSupportFire>
	<CannotBeChosen>true</CannotBeChosen>
</Row>

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

Code:
local iSmeltingPit = GameInfoTypes.BUILDING_SMELTING_PIT
local iSmeltDummy = GameInfoTypes.BUILDING_SMELTING_DUMMY
[color="magenta"]local pCity = plot:GetPlotCity();
local iNewOwner = cCity:GetOwner();
local iOldOwner = cCity:GetOriginalOwner();
local iPreviousOwner = cCity:GetPreviousOwner();[/color]
local iCiv = GameInfoTypes.CIVILIZATION_DECEPTICON

function SmeltingPitExtraProduction[color="red"](Arguments are Missing)[/color]
	if [COLOR="Red"]pPlayer[/COLOR]:GetCivilizationType() == iCiv then
		[color="red"]if iNewOwner ~= iOldOwner then[/color]
			if (pCity:GetNumBuilding(iSmeltingPit) > 0) then
				print("This city has a Smelting Pit and it's been captured by the Decepticons!")
				pCity:SetNumRealBuilding(iSmeltDummy[color="red"], You Need an Integer Value Here[/color])
				print("A Smetling Pit Dummy Building has been added!")
			end
		end
	end
[color="red"]end
There's no event hook that makes the function execute[/color]
The problems with the bits shown in magenta are:
  • without knowing what type of game event 'hook' the function called SmeltingPitExtraProduction will be fired by there is no way to know what these even mean, if anything.
  • It is generally better to place any such bits of code within the function where the bits will be used, because as you have them they will be executed at saved-game loading or new-game start and will be therefore 'nil'. And then when you use them you will be attempting to manipulate values that are 'nil'.
  • You are also referencing data that does not exist at the time when it is being referenced:
    Code:
    local iNewOwner = [COLOR="Red"]cCity[/COLOR]:GetOwner();
    local iOldOwner = [COLOR="red"]cCity[/COLOR]:GetOriginalOwner();

The problems with the bits shown in red are:
  • There are no "()" characters with the function name and there is no list of arguments the function is accepting from whatever activated it, so there is no data for the function to "run on", as it were.
  • You have never defined what pPlayer is
  • Since both iNewOwner and iOldOwner wil have a value of 'nil' because they have never been properly defined, this will never be seen as being 'true' and therefore the rest of the code will never execute:
    Code:
    [color="red"]if iNewOwner ~= iOldOwner then[/color]
  • In this line you've omitted the comma needed to seperate the 1st from the 2nd argument, and you've omitted to state how many of the building should be added to the city:
    Code:
    pCity:SetNumRealBuilding(iSmeltDummy[color="red"], You Need an Integer Value Here[/color])
  • You are missing an 'end' command
  • There is nothing in your code telling the game when it should execute the function called SmeltingPitExtraProduction (ie, as mentioned, there is no event hook you are adding your function to)
 
With the missing event, is there a event that can detect a owner of a city and who founded it and when a building is built? And how about the arguments?

EDIT: Nevermind, I think I found it. Is it City.GetOwner?
 
You are confusing lua GameEvents with lua city methods.

In addition to GameEvents, there are Events and LuaEvents

So, for example, if we want a function to run every turn, we use GameEvents.PlayerDoTurn. This 'hook' passes one argument to any function that is set-up to run off the PlayerDoTurn game event: this argument is the Player ID# for the player the game is currently processing during the stuff that happens when you as the user press NEXT TURN. Each valid player within a game is given a pre-assigned Player ID# for the duration of that game: this includes Human, AI Major Civs, City-States, and Barbarians. Each of these is processed in turn when you click the NEXT TURN button -- part of that processing is to execute for each of these players any function that has been 'subscribed' to run as a PlayerDoTurn event. We do this like this:
Code:
function SomeFunctionName(PlayerID)
      ----some code stuff we want the game to do
end
GameEvents.PlayerDoTurn.Add(SomeFunctionName)
or​
Code:
GameEvents.PlayerDoTurn.Add(function(PlayerID)
	----some code stuff we want the game to do
end)
One of the usual things we want to do is translate the PlayerID # given into a player pointer object because all of the 'Player' methods available to us in the game's lua system require us to state player in this form, for the most part, so we do this nearly always as a first thing:​
Code:
local iCiv = GameInfoTypes.CIVILIZATION_DECEPTICON

function SomeFunctionName(PlayerID)
	local pPlayer = Players[PlayerID]
	----and now we have a valid 'object pointer' we can use in methods like this:
	if pPlayer:GetCivilizationType() == iCiv then
		--do some more even more neat-o stuff when the player currently taking their turn is our civilization
	end
end
GameEvents.PlayerDoTurn.Add(SomeFunctionName)
 
OK, LeeS, I tried to follow what you just said. How does it look now?

Code:
local iSmeltingPit = GameInfoTypes.BUILDING_SMELTING_PIT
local iSmeltDummy = GameInfoTypes.BUILDING_SMELTING_DUMMY
local iCiv = GameInfoTypes.CIVILIZATION_DECEPTICON

local iCity = plot:GetPlotCity();
local iNewOwner = iCity:GetOwner();
local iOldOwner = iCity:GetOriginalOwner();
local pPlayer = Players[iPlayer];
local pNewPlayer = Players[iNewPlayer];

function SmeltingPitExtraProduction(PlayerID)
	local pPlayer = Players[PlayerID]
	if pPlayer:GetCivilizationType() == iCiv then
		if iNewOwner ~= iOldOwner then 
		print("This city appears to captured by the Decepticons!")
			if (pCity:GetNumBuilding(iSmeltingPit) > 0) then
                        print("It appears that there's Smelting Pit building in this city!")
				pCity:SetNumRealBuilding (iSmeltDummy, 1)
				print("A Smetling Pit Dummy Building has been added!")
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(SmeltingPitExtraProduction)
 
OK, LeeS, I tried to follow what you just said. How does it look now?

Code:
local iSmeltingPit = GameInfoTypes.BUILDING_SMELTING_PIT
local iSmeltDummy = GameInfoTypes.BUILDING_SMELTING_DUMMY
local iCiv = GameInfoTypes.CIVILIZATION_DECEPTICON

[COLOR="red"]local iCity = plot:GetPlotCity();
local iNewOwner = iCity:GetOwner();
local iOldOwner = iCity:GetOriginalOwner();
local pPlayer = Players[iPlayer];
local pNewPlayer = Players[iNewPlayer];[/COLOR]

function SmeltingPitExtraProduction(PlayerID)
	local pPlayer = Players[PlayerID]
	if pPlayer:GetCivilizationType() == iCiv then
		if [COLOR="Red"]iNewOwner[/COLOR] ~= [COLOR="red"]iOldOwner[/COLOR] then 
		print("This city appears to captured by the Decepticons!")
			if ([COLOR="Green"]pCity[/COLOR]:GetNumBuilding(iSmeltingPit) > 0) then
                        print("It appears that there's Smelting Pit building in this city!")
				[COLOR="green"]pCity[/COLOR]:SetNumRealBuilding (iSmeltDummy, 1)
				print("A Smetling Pit Dummy Building has been added!")
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(SmeltingPitExtraProduction)
Better, but you still have errors and problems that were outlined in Post 36

pCity shown in green has never been defined anywhere within function SmeltingPitExtraProduction, and the red bits are either going to get nil as their value or are going to cause runtime errors causing the code to fail, or they aren't going to work because two values that are equal to 'nil' will never be seen as being anything but equal to each other (nil == nil), like as you have here:
Code:
if iNewOwner ~= iOldOwner then

In order to look through all of a player's cities you do like as this:
Code:
function SmeltingPitExtraProduction(PlayerID)
	local pPlayer = Players[PlayerID]
	if pPlayer:GetCivilizationType() == iCiv then
		for pCity in pPlayer:Cities() do
			--here we can do something specific in each of the player's cities, like get whether the current city owner is the same as the city's original owner
		end
	end
end
GameEvents.PlayerDoTurn.Add(SmeltingPitExtraProduction)
so we can then get each city's original owner like this:​
Code:
function SmeltingPitExtraProduction(PlayerID)
	local pPlayer = Players[PlayerID]
	if pPlayer:GetCivilizationType() == iCiv then
		for pCity in pPlayer:Cities() do
			local iOriginalCityOwner = pCity:GetOriginalOwner()
		end
	end
end
GameEvents.PlayerDoTurn.Add(SmeltingPitExtraProduction)
This gives us the Player ID# of the city's original owner as an integer value, and since the city's current owner cannot be any other player ID # than the one passed as PlayerID we can do this:​
Code:
function SmeltingPitExtraProduction(PlayerID)
	local pPlayer = Players[PlayerID]
	if pPlayer:GetCivilizationType() == iCiv then
		for pCity in pPlayer:Cities() do
			local iOriginalCityOwner = pCity:GetOriginalOwner()
			if iOriginalCityOwner ~= PlayerID then
				--here is where the stuff to add the building in this case would go.
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(SmeltingPitExtraProduction)
And you do not need the stuff I kept objecting to, so the code to this point becomes:​
Code:
local iSmeltingPit = GameInfoTypes.BUILDING_SMELTING_PIT
local iSmeltDummy = GameInfoTypes.BUILDING_SMELTING_DUMMY
local iCiv = GameInfoTypes.CIVILIZATION_DECEPTICON

function SmeltingPitExtraProduction(PlayerID)
	local pPlayer = Players[PlayerID]
	if pPlayer:GetCivilizationType() == iCiv then
		for pCity in pPlayer:Cities() do
			local iOriginalCityOwner = pCity:GetOriginalOwner()
			if iOriginalCityOwner ~= PlayerID then
				--here is where the stuff to add the building in this case would go.
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(SmeltingPitExtraProduction)
and I'm leaving it to you to place this:​
Code:
print("This city appears to captured by the Decepticons!")
if (pCity:GetNumBuilding(iSmeltingPit) > 0) then
	print("It appears that there's Smelting Pit building in this city!")
	pCity:SetNumRealBuilding(iSmeltDummy, 1)
	print("A Smetling Pit Dummy Building has been added!")
end
where I left this line​
Code:
--here is where the stuff to add the building in this case would go.
 
Back
Top Bottom