Inquiriae Swiftia about Lua

You could iterate through the players' Cities to figure that one out.

Problem is that City ID's are not unique, and it's dangerous to store their game objects through save games I believe, so your only option then is to manually build an ID through some combination of player ID and city ID, then use string substitution afterward to figure out which city to look for when you go over it again.

I also have no idea if CityConstructed fires when a building is given directly through Lua, although again, when given through Lua you should be able to modify the function to also add pertinent details to the same table.
 
If I'm reading this correctly:
Code:
PlayersWithBuildingX = {}

function AWonderCompleted(ownerId, cityId, buildingType, bGold, bFaithOrCulture)
	local WonderDetails = GameInfo.Buildings{ID=buildingType}()
	sFreeBuildingThisCity = WonderDetails.FreeBuildingThisCity
	if sFreeBuildingThisCity ~= "NULL" then
		if sFreeBuildingThisCity == "BUILDINGCLASS_X" then
			PlayersWithBuildingX[ownerId] = "true"
		end
	end
end
GameEvents.CityConstructed.Add(AWonderCompleted)
Won't this determine which players, not cities, have Building X?

yup. Because if you want to know whether player_X owns all the buildings, you don't really care which city the building is in, only that at least one city controlled by a player has the building.

You would also need to add a CityCaptureComplete event to look at whether a captured city had the building, and update to the players within the table that are set to "true". As part of a city captured event you could also run through all the cities of the player that lost the city to see if that was the only city controlled by the city-loser that had the building. But you would need to add a CityCaptureComplete event in any case unless you set BUILDING_X to be never captured. But in the case of buildings given by wonders (ie, FreeBuildingThisCity) I'm not sure if the game ignores the <NeverCapture> and keeps the free building regardless.

You would also need to look at cities that are being razed, but you would have to do so whether you simply track by player_A has 1+ buildings or if you track by individual cities having the building.
 
I also have no idea if CityConstructed fires when a building is given directly through Lua, although again, when given through Lua you should be able to modify the function to also add pertinent details to the same table.
It appears to not. Which is why you weren't able to see any change in data in my Knights Templar when you used Live Tuner / IGE to "plop" the wonder into a kity.
 
So to clarify another point:
Code:
iNumItemsInTable = 0
	bAllAreOwnedByPlayerX = true
	for k,v in pairs(PlayersWithBuildingX) do
		iNumItemsInTable = iNumItemsInTable + 1
		if k ~= iPlayer then
			bAllAreOwnedByPlayerX = false
		end
	end
	if iNumItemsInTable == 0 then
		bAllAreOwnedByPlayerX = false
	end
This is essentially checking to see that every player ID who has at least one Building X all point to Player X? To that point, if it does so, that would effectively remove the need to check every city's buildings, right?
 
So to clarify another point:
Code:
iNumItemsInTable = 0
	bAllAreOwnedByPlayerX = true
	for k,v in pairs(PlayersWithBuildingX) do
		iNumItemsInTable = iNumItemsInTable + 1
		if k ~= iPlayer then
			bAllAreOwnedByPlayerX = false
		end
	end
	if iNumItemsInTable == 0 then
		bAllAreOwnedByPlayerX = false
	end
This is essentially checking to see that every player ID who has at least one Building X all point to Player X? To that point, if it does so, that would effectively remove the need to check every city's buildings, right?
Yes, it will remove the need to check through every city on the map to determine how many buildings there are and who controls each individual building.

Since I placed it within the PlayerDoTurn function this will run for every player, and if that player happens to control all the buildings then bAllAreOwnedByPlayerX will be true. You can then use that to determine what else should happen.

So during the human player's turn, the iPlayer will have a value of "0", and on some AI player's turn it might have a value of "6", for example. So on the human player's turn, if one of the "k" values in the table PlayersWithBuildingX is "4", for player 4, then the result for the human player would be "false" for bAllAreOwnedByPlayerX. In such an example, if and only if player 4 has any of the buildings would bAllAreOwnedByPlayerX result in "true".

The first time the lua code runs and adds a BUILDING_X to a city, or a wonder was constructed that gave BUILDING_X, then on the very next turn (at the latest) bAllAreOwnedByPlayerX will be true for the player that was given the building via lua or got one from having a wonder constructed in a city. But this condition will still occur whether you try to use my method or if you try to track based on individual cities.

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

EDIT:

Having said all that it might not still be a bad idea to look through every player's cities during their PlayerDoTurn as a clean-up measure to ensure that the info that has been saved to PlayersWithBuildingX is up-to-date. All you would be looking for in that case is whether the player has no building_X in any city. If the player has at least one building_X then you update that player's PlayersWithBuildingX[iPlayer] to "true", and if the player has none, remove that player's PlayersWithBuildingX[iPlayer] from the table. If I remember correctly you would do the remove as:
Code:
table.remove(PlayersWithBuildingX , iPlayer)
 
So, I was actually planning to make a new victory type only available to one civilization which requires them to control Wonder X and all cities with Building X for 10 consecutive turns. (Hopefully this turns out to be harder than it sounds)
So, it should be as simple as this, then:
Spoiler :
Code:
BuildingX = GameInfoTypes.BUILDING_X
BuildingClassX = GameInfoTypes.BUILDINGCLASS_X
WonderX = GameInfoTypes.WONDER_X
CivX = GameInfoTypes.CIVILIZATION_X
VictoryX = GameInfoTypes.VICTORY_X
SetPersistentProperty("VictoryCountdown", 0)

include("NewSaveUtils")

PlayersWithBuildingX = {}

function WonderCompleted(ownerId, cityId, buildingType, bGold, bFaithOrCulture)
	local WonderDetails = GameInfo.Buildings{ID=buildingType}()
	sFreeBuildingThisCity = WonderDetails.FreeBuildingThisCity
	if sFreeBuildingThisCity ~= "NULL" then
		if sFreeBuildingThisCity == BuildingClassX then
			PlayersWithBuildingX[ownerId] = "true"
		end
	end
end
GameEvents.CityConstructed.Add(AWonderCompleted)


function IsHasWonder(playerID, wonderType)
	local pPlayer = Players[iPlayer]
	local b_HasWonder = false
	for pCity in pPlayer:Cities() do
		if (pCity:IsHasWonder(wonderType)) then
			b_HasWonder = true
		end
	end
	return b_HasWonder
end

function NewCivXVictory(iPlayer)
	local pPlayer = Players[iPlayer]
	local VictoryTurnsLeft = GetPersistantProperty("VictoryCountdown") -- this is just a shortcut, especially for math
	if (pPlayer:GetCivilizationType() == CivX) then
	
		iNumItemsInTable = 0
		bAllAreOwnedByPlayerX = true
		for k,v in pairs(PlayersWithBuildingX) do
			iNumItemsInTable = iNumItemsInTable + 1
			if k ~= iPlayer then
				bAllAreOwnedByPlayerX = false
			end
		end
		if iNumItemsInTable == 0 then
			bAllAreOwnedByPlayerX = false
		end
		
		if bAllAreOwnedByPlayerX and (VictoryTurnsLeft < 10) then
			SetPersistentProperty("VictoryCountdown", VictoryTurnsLeft + 1)
		elseif bAllAreOwnedByPlayerX and (VictoryTurnsLeft == 10) and (IsHasWonder(iPlayer, WonderX) == true) then
			Game.SetWinner(pPlayer:GetTeam(), VictoryX)
		else -- since there's no scripting that allows VictoryCountdown to go below 0, this is really just "if not bAllAreOwnedByPlayerX then"
			SetPersistentProperty("VictoryCountdown", 0) -- Reset the countdown; player must start all over again
		end
end
GameEvents.PlayerDoTurn.Add(NewCivXVictory)
Using save utils so the 10 turns don't have to reset across games. I don't believe it's necessary for the table to be saved, since it's checked every turn anyway. And I couldn't find a Player.IsHasWonder() method or anything like it, so I made my own. Hopefully it's right.

Now as for actually finding all cities with Building X... I will need that too, not for the victory but for something that will help Civ X achieve Victory X. I'm having a bit of trouble constructing that part...
Spoiler :
Code:
CitiesWithBuildingX = {} -- parameters: iOwner, x, y
function AllCitiesWithBuildingX(_)
	for iPlayer in PlayersWithBuildingX do -- This is the line I'm pretty sure is most wrong
		local pPlayer = Players[iPlayer]
		for pCity in pPlayer:Cities() do
			if (pCity:IsHasBuilding(BuildingX)) then
				CitiesWithBuildingX[iPlayer, pCity:GetPlot():GetX(), pCity:GetPlot():GetY()] = true
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(AllCitiesWithBuildingX)
But then... how do I access the x and y later? Because I'm going to need that...
 
I'll have to wait until later this evening to make a more substantive answer (because of supper and reasons) unless someone else answers beforehand.

But a wonder is a building. Use the same city:IsHasBuilding() method as you would for any other building.


-----------------------------------------------------------------------------------------------------------------------------------
[edit add-on]

What exactly are you trying accomplish with this part of the code? By that, I mean can you describe what you are wanting to do?
Code:
CitiesWithBuildingX = {} -- parameters: iOwner, x, y
function AllCitiesWithBuildingX(_)
	for iPlayer in PlayersWithBuildingX do -- This is the line I'm pretty sure is most wrong
		local pPlayer = Players[iPlayer]
		for pCity in pPlayer:Cities() do
			if (pCity:IsHasBuilding(BuildingX)) then
				CitiesWithBuildingX[iPlayer, pCity:GetPlot():GetX(), pCity:GetPlot():GetY()] = true
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(AllCitiesWithBuildingX)

--------------------------------------------------------------------------------------------------------------------------------------------------
[additional edit add-on]

I would make the following small changes to the other part of the code (all highlighted in blue).
Spoiler :
Code:
BuildingX = GameInfoTypes.BUILDING_X
BuildingClassX = [color="blue"]"BUILDINGCLASS_X"[/color]
	--you want to compare against the text string from the column "FreeBuildingThisCity" in <Buildings> XML table, so you need this to be a text string
	-- "GameInfoTypes." turns it into an integer ID# from the <Buildingclasses> XML table, not at all what you want
WonderX = GameInfoTypes.[color="blue"]BUILDING_[/color]WONDER_X
CivX = GameInfoTypes.CIVILIZATION_X
VictoryX = GameInfoTypes.VICTORY_X
SetPersistentProperty("VictoryCountdown", 0)

include("NewSaveUtils")

PlayersWithBuildingX = {}

function WonderCompleted(ownerId, cityId, buildingType, bGold, bFaithOrCulture)
	local WonderDetails = GameInfo.Buildings{ID=buildingType}()
	sFreeBuildingThisCity = WonderDetails.FreeBuildingThisCity
	if sFreeBuildingThisCity ~= "NULL" then
		if sFreeBuildingThisCity == BuildingClassX then
			PlayersWithBuildingX[ownerId] = "true"
		end
	end
end
GameEvents.CityConstructed.Add([color="blue"]WonderCompleted[/COLOR])


function IsHasWonder(playerID, wonderType)
	local pPlayer = Players[[color="blue"]playerID[/color]]
	local b_HasWonder = false
	for pCity in pPlayer:Cities() do
		if pCity:[color="blue"]IsHasBuilding[/color](wonderType) then
			b_HasWonder = true
		end
	end
	return b_HasWonder
end

function NewCivXVictory(iPlayer)
	local pPlayer = Players[iPlayer]
	local VictoryTurnsLeft = GetPersistantProperty("VictoryCountdown") -- this is just a shortcut, especially for math
	if (pPlayer:GetCivilizationType() == CivX) then
	
		iNumItemsInTable = 0
		bAllAreOwnedByPlayerX = true
		for k,v in pairs(PlayersWithBuildingX) do
			iNumItemsInTable = iNumItemsInTable + 1
			if k ~= iPlayer then
				bAllAreOwnedByPlayerX = false
			end
		end
		if iNumItemsInTable == 0 then
			bAllAreOwnedByPlayerX = false
		end
		
		if bAllAreOwnedByPlayerX and (VictoryTurnsLeft < 10) then
			SetPersistentProperty("VictoryCountdown", VictoryTurnsLeft + 1)
		elseif bAllAreOwnedByPlayerX and (VictoryTurnsLeft == 10) and (IsHasWonder(iPlayer, WonderX) == true) then
			Game.SetWinner(pPlayer:GetTeam(), VictoryX)
		else -- since there's no scripting that allows VictoryCountdown to go below 0, this is really just "if not bAllAreOwnedByPlayerX then"
			SetPersistentProperty("VictoryCountdown", 0) -- Reset the countdown; player must start all over again
		end
	[color="blue"]end[/color]
end
GameEvents.PlayerDoTurn.Add(NewCivXVictory)
 
But a wonder is a building. Use the same city:IsHasBuilding() method as you would for any other building.
The point is that, even so, I need a Player.IsHasBuilding() method.

[edit add-on]

What exactly are you trying accomplish with this part of the code? By that, I mean can you describe what you are wanting to do?
Code:
CitiesWithBuildingX = {} -- parameters: iOwner, x, y
function AllCitiesWithBuildingX(_)
	for iPlayer in PlayersWithBuildingX do -- This is the line I'm pretty sure is most wrong
		local pPlayer = Players[iPlayer]
		for pCity in pPlayer:Cities() do
			if (pCity:IsHasBuilding(BuildingX)) then
				CitiesWithBuildingX[iPlayer, pCity:GetPlot():GetX(), pCity:GetPlot():GetY()] = true
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(AllCitiesWithBuildingX)
Per DarkScythe's suggestion, I was trying to put all the cities with Building X in a table by adding their owners' playerIDs, x coords, and y coords, since another function will need to be able to find all cities with Building X, and this time, it really does need to pinpoint individual cities - not just players.

As for the code edits, most of them involve just careless mistakes I threw in, it looks like.
 
I can't test without Building_X or Wonder_X, but try as follows:
Spoiler :
Code:
BuildingX = GameInfoTypes.BUILDING_X
BuildingClassX = "BUILDINGCLASS_X"
WonderX = GameInfoTypes.BUILDING_WONDER_X
CivX = GameInfoTypes.CIVILIZATION_X
VictoryX = GameInfoTypes.VICTORY_X
SetPersistentProperty("VictoryCountdown", 0)

include("NewSaveUtils")

PlayersWithBuildingX = {}
CitiesWithBuildingX = {}

-----------------------------------------------------------------------
--following will store all city plots for player "iPlayer" that have Building_X within a subtable of CitiesWithBuildingX
--	the subtable name will be of format CitiesWithBuildingX[iPlayer] with the player number as "iPlayer"
--	cities within the player subtable CitiesWithBuildingX[0] (for the human player) will be recorded by the cities' plot ID
--will also update table PlayersWithBuildingX based on whether any city of player "iPlayer" has Building_X
-----------------------------------------------------------------------

function AllCitiesWithBuildingX(iPlayer)
	local pPlayer = Players[iPlayer]
	[COLOR="Blue"]if pPlayer:GetNumCities() < 1 then return end[/COLOR]
	bPlayerHasBuildingX = false
	for pCity in pPlayer:Cities() do
		if (pCity:IsHasBuilding(BuildingX)) then
			pCityPlot = pCity:Plot()
			bPlayerHasBuildingX = true
			if CitiesWithBuildingX[iPlayer] == nil then
				CitiesWithBuildingX[iPlayer] = { pCityPlot }
			else
				CityAlreadyInTable = false
				for k,v in pairs (CitiesWithBuildingX[iPlayer]) do
					if v == pCityPlot then
						CityAlreadyInTable = true
					end
				end
				if not CityAlreadyInTable then
					table.insert(CitiesWithBuildingX[iPlayer], pCityPlot)
				end
			end
		else	--city does not have the building, but clean-up table if the city was listed as having the building
			if CitiesWithBuildingX[iPlayer] ~= nil then
				local iRemoveItem = 0
				for k,v in pairs (CitiesWithBuildingX[iPlayer]) do
					if v == pCityPlot then
						iRemoveItem = k
					end
				end
				table.remove(CitiesWithBuildingX[iPlayer], iRemoveItem)
			end
		end
	end
	if bPlayerHasBuildingX then
		PlayersWithBuildingX[iPlayer] = "true"
	else
		if CitiesWithBuildingX[iPlayer] ~= nil then
			table.remove(CitiesWithBuildingX, iPlayer)
		end
		if PlayersWithBuildingX[iPlayer] ~= nil then 
			table.remove(PlayersWithBuildingX, iPlayer)
		end
	end
end

---------------------------------------------------------------------------------------------------
--rebuild tables PlayersWithBuildingX and CitiesWithBuildingX on mod loading
---------------------------------------------------------------------------------------------------
function ModLoading()
	for i = 0, GameDefines.MAX_MAJOR_CIVS - 1 do
		local iSlot = PreGame.GetSlotStatus(i)
		if iSlot == SlotStatus.SS_TAKEN or iSlot == SlotStatus.SS_COMPUTER then
			local pPlayer = Players[i]
			if pPlayer:IsAlive() then
				AllCitiesWithBuildingX(i)
			end
		end
	end
end
Events.LoadScreenClose.Add(ModLoading)

function WonderCompleted(ownerId, cityId, buildingType, bGold, bFaithOrCulture)
	local WonderDetails = GameInfo.Buildings{ID=buildingType}()
	sFreeBuildingThisCity = WonderDetails.FreeBuildingThisCity
	if sFreeBuildingThisCity ~= "NULL" then
		if sFreeBuildingThisCity == BuildingClassX then
			PlayersWithBuildingX[ownerId] = "true"
		end
	end
end
GameEvents.CityConstructed.Add(WonderCompleted)

function IsHasWonder(playerID, wonderType)
	local pPlayer = Players[playerID]
	local b_HasWonder = false
	for pCity in pPlayer:Cities() do
		if pCity:IsHasBuilding(wonderType) then
			b_HasWonder = true
		end
	end
	return b_HasWonder
end

function NewCivXVictory(iPlayer)
	local pPlayer = Players[iPlayer]
	local VictoryTurnsLeft = GetPersistantProperty("VictoryCountdown") -- this is just a shortcut, especially for math
	if not pPlayer:IsAlive() then return end
	if pPlayer:IsMinorCiv() then return end
	if pPlayer:IsBarbarian() then return end
	AllCitiesWithBuildingX(iPlayer) --update the data for cities with Building X before executing next line
	if (pPlayer:GetCivilizationType() ~= CivX) then return end
	
	iNumItemsInTable = 0
	bAllAreOwnedByPlayerX = true
	for k,v in pairs(PlayersWithBuildingX) do
		iNumItemsInTable = iNumItemsInTable + 1
		if k ~= iPlayer then
			bAllAreOwnedByPlayerX = false
		end
	end
	if iNumItemsInTable == 0 then
		bAllAreOwnedByPlayerX = false
	end
		
	if bAllAreOwnedByPlayerX and (VictoryTurnsLeft < 10) then
		SetPersistentProperty("VictoryCountdown", VictoryTurnsLeft + 1)
	elseif bAllAreOwnedByPlayerX and (VictoryTurnsLeft == 10) and (IsHasWonder(iPlayer, WonderX) == true) then
		Game.SetWinner(pPlayer:GetTeam(), VictoryX)
	else -- since there's no scripting that allows VictoryCountdown to go below 0, this is really just "if not bAllAreOwnedByPlayerX then"
		SetPersistentProperty("VictoryCountdown", 0) -- Reset the countdown; player must start all over again
	end

end
GameEvents.PlayerDoTurn.Add(NewCivXVictory)
I saved the city plot info for any city that had the building_X, rather than saving city name, plot_X, plot_Y. Not sure where you were planning on using the plot info, but you can pretty easily extract the plot X & Y from the plot ID, as well as the city info from the plot data.


----------------------------------------------
[edit]
One thing I just realised is I didn't add anything to account for cities getting captured or razed, so it's possible in the code as I wrote it that CitiesWithBuildingX[iPlayer] could have an extra item listed in it for a city that player lost to conquest or a city that player razed, if that player also still has a city with Building_X

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

[2nd edit]

note the small change (highlighted in blue) I made. the need for such a change occurred to me this morning. Still need to think about captured and razed cities.
 
I supposed for testing purposes it could be any building, wonder and victory combination, since it won't be permanent.
So to retrieve all the plots whose cities have Building X, I would do... something like this?
Code:
blah blah blah

for i = 0, GameDefines.MAX_MAJOR_CIVS -1, 1 do
	for plotID in CitiesWithBuildingX[i] do
		local pPlot = Map.GetPlotByIndex(plotID)
		local pCity = pPlot:GetPlotCity()

		blah blah blah
	end
end
 
We've already stored the plot info so shouldn't need to do the map thing:
Code:
for i = 0, GameDefines.MAX_MAJOR_CIVS -1, 1 do
	if CitiesWithBuildingX[i] ~= nil then
		for k,v in pairs (CitiesWithBuildingX[i]) do
			local pPlot = v
			if pPlot:IsCity() then -- extra insurance against lua runtime failure for a razed and now dead city 
				local pCity = pPlot:GetPlotCity()

				blah blah blah
			end
		end
	end
end
But it really depends on when you want to do this "looking". If you are only interested in the cities for player X during player X's turn, you wouldn't need to look through all cities of all players. You would just look through the cities of player X as part of a PlayerDoTurn, and you'd restructure as:
Code:
if CitiesWithBuildingX[iPlayer] ~= nil then
	for k,v in pairs (CitiesWithBuildingX[iPlayer]) do
		local pPlot = v
		if pPlot:IsCity() then -- extra insurance against lua runtime failure for a razed and now dead city 
			local pCity = pPlot:GetPlotCity()

			blah blah blah
		end
	end
end
Just remember that so much of the lua "stuff" that you can use to change in-game conditions are tied to the player in a pPlayer form after you do local pPlayer = Players[iPlayer] that the first chunk of code might not make in-game effects properly if you don't add a line or two for it as like the following:
Code:
for i = 0, GameDefines.MAX_MAJOR_CIVS -1, 1 do
	[color="blue"]local pPlayer = Players[i]
	if pPlayer:IsAlive() then[/color]
		if CitiesWithBuildingX[i] ~= nil then
			for k,v in pairs (CitiesWithBuildingX[i]) do
				local pPlot = v
				if pPlot:IsCity() then -- extra insurance against lua runtime failure for a razed and now dead city 
					local pCity = pPlot:GetPlotCity()

					blah blah blah
				end
			end
		end
	[color="blue"]end[/color]
end
You would also need, I think, to add in the
Code:
local iSlot = PreGame.GetSlotStatus(i)
if iSlot == SlotStatus.SS_TAKEN or iSlot == SlotStatus.SS_COMPUTER then
as I did in the iteration though the for i = 0, GameDefines.MAX_MAJOR_CIVS -1, 1 do (as I did in post # 29, I mean)
 
Necro!
So I'm working on this:
Enemy units expend movement points twice as fast in your territory.
I asked about this a while back and ThorHammerz began explaining the DLL algorithm for the Great Wall's <BorderObstacle>, except I have no intention of modding the DLL. So I'm trying to develop a new, crude algorithm with lua...
Basically, when an enemy unit moves into your territory, it would count their normal moves and divide that number in two to get their modified move number, and then if the moves they've already used up is greater than or equal to this modified move number, then the unit is instantly forced to stop.

Does that sound viable? Overly simplistic, perhaps? At any rate, I have this right now:
Code:
 GameEvents.UnitSetXY.Add(
function(playerID, iUnit, iX, iY)
	for i, iPlayer in pairs(Players) do
		local pPlayer = Players[iPlayer] -- This is you...
		local mPlayer = Players[playerID] -- ...and this is the unit owner
		if (pPlayer == mPlayer) then return end
		if (Map.GetPlot(iX, iY):GetOwner() == playerID) then return end
		if not (GameInfo.Leader_Traits{LeaderType={GameInfo.Civilization_Leaders{CivilizationType={GameInfo.Civilizations{ID=Players[Map.GetPlot(iX, iY):GetOwner()]:GetCivilizationType()}}}}} == GameInfoTypes.TRAIT_X) then return end
		mUnit = mPlayer:GetUnitByID(iUnit)
		iDefaultMoves = GameInfo.Units{ID=mUnit:GetUnitType()}.Moves
		iNewMoves = math.floor(iDefaultMoves / 2)
		iMovesUsed = (iDefaultMoves - mUnit:GetMoves())
		if (iMovesUsed >= iNewMoves) then
			mUnit:SetMoves(0)
		end
	end
end)
I'm not sure I found the extra civ right. Any suggestions, or does the code look good as is?
 
Line 3:

Code:
for i, iPlayer in pairs(Players) do

Where is your function getting iPlayer from? That function would halt there and be unable to continue. UnitSetXY will fire for every unit every time they move one tile, just so you're aware of the potential overhead.
 
Line 3:

Code:
for i, iPlayer in pairs(Players) do

Where is your function getting iPlayer from? That function would halt there and be unable to continue. UnitSetXY will fire for every unit every time they move one tile, just so you're aware of the potential overhead.
he's trying to set up an lua-table "for" loop on the table Players, and instead of using k,v as the variables he's using i, iPlayer.

never tried to manipulate the "Players" table like that so don't know if it will work like any other lua table or not. In any case I'd move this:
Code:
local mPlayer = Players[playerID]
up above the "for....." line so you don't have to re-create the same info over and over again.

But I don't really think you need the "for....do...end" loop.

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

Code:
iRequiredCivilization = GameInfoTypes.CIVILIZATION_X

GameEvents.UnitSetXY.Add(
function(playerID, iUnit, iX, iY)
	local pPlot = Map.GetPlot(iX, iY)
	local iPlotOwner = pPlot:GetOwner()
	if iPlotOwner == playerID then return end
	if Players[iPlotOwner]:GetCivilizationType() ~= iRequiredCivilization then return end
	local pPlotOwnerPlayer = Players[iPlotOwner]
	local pUnitOwner = Players[playerID]
	local pUnit = pUnitOwner:GetUnitByID(iUnit)
	local iHomeTeam = pPlotOwnerPlayer:GetTeam()
	local iVisitingTeam = pUnitOwner:GetTeam()
	local bWar = Teams[iHomeTeam]:IsAtWar(iVisitingTeam)
	if bWar then
		[color="red"]local iDefaultMoves = GameInfo.Units{ID=pUnit:GetUnitType()}.Moves[/color]
		local iNewMoves = math.floor(iDefaultMoves / 2)
		local iMovesUsed = (iDefaultMoves - pUnit:GetMoves())
		if (iMovesUsed >= iNewMoves) then
			pUnit:SetMoves(0)
		end
	end
end)

the line in red you'll have to do some checking on. You don't get "2" when a unit with 2 moves has not moved yet and you do a pUnit:GetMoves(). You get "120". I think you'd be safe to just multiply by 60.
 
Ah, sorry.

Whenever I see a line starting with "for i ..." I immediately decide it's going to be an integer loop.

However, while that will iterate the table just fine, the values you get are deceptive because the "namings" are incorrect.

Ideally, it needs to be:
Code:
for iPlayer, pPlayer in pairs(Players) do

Remember, whenever you normally try to define pPlayer, you do so by giving it the value of Players[iPlayer] -- in other words, look in the Players table for the value at table index number iPlayer, which happens to be a table of other values. In this case, iPlayer is our key, since we're doing a key/value pairs lookup.

This would mean that your following line would still fail because you want to find pPlayer with Players[iPlayer] when iPlayer is already the pPlayer table! If you are already iterating over the Players table, why go through the trouble of setting a variable that has to look through it again?

If you're iterating over the entire Players table, you should use the above syntax instead for clarity and sanity, then simply check the pPlayer table directly. Actually, even then, how the heck did you arrive at "--this is you"? There's no Civ check, or anything at all.

Code:
for iPlayer, pPlayer in pairs(Players) do
if pPlayer:GetCivilizationType == GameInfoTypes.MY_AWESOME_SECRET_CIV then
...

The random one-line return ends also annoy me to no end, but that aside, it feels like you're making this way more complicated than you have to.

Anyway, as far as the line LeeS highlighted in red:
You are correct that unit:GetMoves() and related methods don't actually return the number of moves a unit has as we're familiar with them -- they're multiples of 60. However, the code isn't actually using that method. It's instead querying the game's Units table (Why? It's really slow.) and grabbing the value from the unit's "Moves" column instead -- this value is the number that we all expect.

Of course, the problem does occur when he attempts to calculate iMovesUsed because he'll get something like "2 - 60" or something.
 
I'm slightly confused now. :crazyeye: Maybe I should normally be completely confused and I just didn't get enough sleep, so it seems like it makes sense.
To clear one thing up, where was I getting this line?
Code:
for i, iPlayer in pairs(Players) do
This was stolen from the lua controlling Pouakai's UA from JFD's The World According to Modders. (Something about free golden age points from DOFs)
Spoiler The Code in Question :
Code:
function JFD_PouakaiGoldenAges(playerID)
	local player = Players[playerID]
	if player:HasPolicy(GameInfoTypes["POLICY_JFD_POUAKAI"]) and player:IsAlive() then
		[COLOR="Blue"]for i, iPlayer in pairs(Players) do[/COLOR]
			if iPlayer:IsAlive() then
				if not iPlayer:IsMinorCiv() then
					if iPlayer ~= player then
						if iPlayer:IsDoF(player) then
							if player:GetGoldenAgeTurns() <= 1 then 
								player:ChangeGoldenAgeTurns(2)
							else
								player:ChangeGoldenAgeTurns(1)
							end

							if iPlayer:GetGoldenAgeTurns() <= 1 then 
								iPlayer:ChangeGoldenAgeTurns(2)
							else
								iPlayer:ChangeGoldenAgeTurns(1)
							end
						end
					end
				end
			end
		end
	end
end
GameEvents.PlayerDoTurn.Add(JFD_PouakaiGoldenAges)
I suppose I took this as a way of pulling an extra player ID out of thin air.
But then reading over what VV said to me once, I now realize that it only iterates over all players, so now I'm even more confused, because then iPlayer would be a pointer, not an ID...
 
This is the problem with simply trying to "steal" code from different mods -- you aren't ever quite sure what they are doing if you aren't able to analyze them properly.

Pouakai's results here are misleading as well, and it had certainly confused you in the process for good reason.

Look at the line immediately after the line you've highlighted in blue:
Code:
if iPlayer:IsAlive() then
Notice anything? The IsAlive() method is supposed to be called from a player object -- in our case, we normally hold the player object in the variable named pPlayer. Pouakai may have misleadingly held the pPlayer table in a variable confusingly named iPlayer, but he knows it is a table for the player object, and is properly using the player methods on it.

Unfortunately, because you are used to seeing iPlayer holding the integer ID, you mistook his usage for it being the iPlayer you are used to. It doesn't matter what the variables are named; He could've said "for pie, dogs in pairs(Players) do" and it would've been just fine as long as he then went with "if dogs:IsAlive() then" -- that second variable declaration is what holds the value of whatever index (key) Lua is looking at within that parent table.

Isn't the whole concept of standardized naming conventions fun?

Here's another way of looking at this:
In lua, since we are looking up key/value pairs, you will frequently see this syntax:
Code:
for k,v in pairs(t) do
This is looping over a table called t, and storing the index in k, and the value in v.

Code:
t[k] = v
t[k] = v
t[k] = v
This is essentially the structure in a very simplified (and redundant) format.

Expand this to the Players table:
Code:
Players[0] = {}
Players[1] = {}
Players[2] = {}

k in this case is 0, 1, or 2 -- the player ID's we're familiar with.
The values they hold would be the table '{}' which would be referred to as v.

If you look closely, you have Players[ID] = {} .. remember how we usually assigned pPlayer?
pPlayer = Players[ID] -- Players[ID] = {}
In other words, pPlayer = {} = v.
 
Necro'd for a very simple question:
Is there an event hook of the same ilk as GameEvents.CityCanConstruct or CityCanTrain, except for improvements? I'd like to make a improvement that can only be built as long as certain conditions are met which aren't possible to account for with just XML. But there doesn't seem to be anything like "GameEvents.UnitCanBuild" here, here, or here... or is there a different function I should be using?
 
I must have added the events to my DLL after Firaxis merged the code ;)

Code:
    <!-- Events sent by plots (v30) -->
    <!--   GameEvents.PlayerCanBuild.Add(function(iPlayer, iUnit, iX, iY, iBuild) return true end) -->
    <!--   GameEvents.PlotCanImprove.Add(function(iX, iY, iImprovement) return true end) -->
    <!--   GameEvents.PlayerBuilding.Add(function(iPlayer, iUnit, iX, iY, iBuild, bStarting) end) (v46) -->
    <!--   GameEvents.PlayerBuilt.Add(function(iPlayer, iUnit, iX, iY, iBuild) end) (v46) -->
 
I had a feeling that would be the answer. :/ So those don't exist in the base DLL?
What about how only European civs being able to build railroads in Scramble for Africa? Can whatever controls that be used? If I could find it...
BTW, is VMC supposed to be compatible with game versions "between 1 and 1"? I have 1.03.# or something after reinstalling, so I can't test my one mod that relies on VMC.
 
Back
Top Bottom