Inquiriae Swiftia about Lua

AW Arcaeca

Deus Vult
Joined
Mar 10, 2013
Messages
3,019
Location
Operation Padlock ground zero
Ain't I soooooooooooo good at Latyn? :p

As the title suggests, if you can get the basic drift of my false Latin, I just have a couple quick lua inquiries:

1. I'd like to be able to access all of pPlayer's units of a specific unit type, but there doesn't seem to be any function to accomplish this. Can I use a function such as this to find all units of that unit type?
Code:
function AW_UnitIterator(pPlayer, iUnitType)
	for pUnit in pPlayer:Units() do
		-- Get all units of iUnitType
		if (pUnit:GetUnitType() == iUnitType) then
			return pUnit
		end
	end
end
2. And then, can I return the number of units of iUnitType that pPlayer has like this?
Code:
SpecificUnit = GameInfoTypes.UNIT_VAGUE_SECRECY_IS_VAGUE
numSpecificUnit = 0

function AW_SpecificUnitNum(pPlayer)
	pUnit = AW_UnitIterator_Type(pPlayer, SpecificUnit)
	for mUnit in pPlayer:Units() do
		if mUnit == pUnit then
			numSpecificUnit = numSpecificUnit + 1
		end
	end
	return numSpecificUnit
end
3. By using these functions, I intend to check if the player has at least one of this unit type and it has at least 40 XP; however, would the following code check for if just one of that unit has 40 XP or if every one has 40 XP? And if it's the latter, how can I change it to the former?
Code:
if (AW_UnitIterator(pPlayer, unitType):GetExperience() >= 40) then


4. Finally, not related as such, but is there some way to select a random city owned by pPlayer? I guess I'm just having a brain fart or something but I can't quite figure this out. :/

TIA,
AW
 
If you just want to count the number of units of X type that have 40 XP then you can use this sort of function:

Code:
function AW_SpecificUnitNum(playerID, unitID, experienceNeeded)
	local player = Players[playerID]
	local numUnits = 0
	for unit in player:Units() do
		if unit:GetUnitType() == unitID then
			if experienceNeeded > 0 then
				if unit:GetExperience() >= experienceNeeded then
					numUnits = numUnits + 1
				end
			else
				numUnits = numUnits + 1
			end
		end
	end
	return numUnits
end

And invoke thus within your function:

Code:
local unitID = GameInfoTypes.UNIT_VAGUE_SECRECY_IS_VAGUE
local experienceNeeded = 40
AW_SpecificUnitNum(playerID, unitID, experienceNeeded)

Set the experienceNeeded to 0 if you just want to count the number of that unit without any XP requirements.

For getting a random city, you create a table to hold the player's cities, and then choose one at random:

Code:
function GetRandom(lower, upper)
    return Game.Rand((upper + 2) - lower, "") + lower
end

function AW_GetRandomCity(playerID)
   local player = Players[playerID]
   local playerCities= {}
   local playerCity = nil
			
   for city in player:Cities() do
	local count = 1
	playerCities[count]=playerID
	count=count+1
   end

   playerCity = playerCities[GetRandom(1, #playerCities)]
   return playerCity 
end
 
JFD's code looks good, so I just wanted to point out the mistakes in your code :)

1. Your function doesn't look like a proper iterator. It just returns the first unit of specific type it finds. How to create a proper iterator in Lua? I don't really know (I'm not good at this language), but you don't need it if you just want to count the number of units of a specific type - a simple loop will do (like JFD's code).

2. Because numSpecificUnit is zeroed only outside of the function, it will get increased every time the function is called. So it needs to be zeroed inside the function (like in JFD's code).
 
467 kudos be upon your head, your Modjesty. :)
However, I still need some way to add a promotion to all units with a Type of unitID. AW_UnitIterator was supposed to return a variable that represented every such unit, and then I could use it to add the promotion like so:
Code:
AW_UnitIterator(pPlayer, unitType):SetHasPromotion(GameInfoTypes.PROMOTION_UNIQUEPROMO, true)
Now that's apparently wrong, so would it be as simple as just doing this to replace it?
Code:
for pUnit in pPlayer:Units() do
	if (pUnit:GetUnitType() == unitType) then
		pUnit:SetHasPromotion(GameInfoTypes.PROMOTION_UNIQUEPROMO, true)
	end
end

EDIT: Oh, and also I'm looking for a way to spawn a couple barbarian units. It doesn't seem like Player.InitUnit or Player.AddFreeUnit would work...or would it? Is there a way to spawn, say, 2 barbs next to the player's city?
 
Yup, iterating through the player's units, checking to see if that unit's type (actually ID) matches up with the desired ID, then grants the promotion will do it.

And for barbs, you can just do Players[63]:InitUnit. Player 63 is always the Barb player.
 
Alright, so to make a couple barbs spawn on a random plot(s) near a city, assuming pCity & iUnitType are already defined (which they are), would be something like this...?
Code:
...

-- Stolen from some of JFD's code
function GetRandom(lower, upper)
    return Game.Rand((upper + 2) - lower, "") + lower
end

local numBarbs = GetRandom(2, 3)
-- Get X & Y coords 
local plotX = pCity:GetX() + GetRandom(-1, 1)
local plotY = pCity:GetY() + GetRandom(-1, 1)

for i = 1, numBarbs do
	Players[63]:InitUnit(iUnitType, plotX, plotY)
end

...
 
Alright, so to make a couple barbs spawn on a random plot(s) near a city, assuming pCity & iUnitType are already defined (which they are), would be something like this...?
Code:
...

-- Stolen from some of JFD's code
function GetRandom(lower, upper)
    return Game.Rand((upper + 2) - lower, "") + lower
end

local numBarbs = GetRandom(2, 3)
-- Get X & Y coords 
local plotX = pCity:GetX() + GetRandom(-1, 1)
local plotY = pCity:GetY() + GetRandom(-1, 1)

for i = 1, numBarbs do
	Players[63]:InitUnit(iUnitType, plotX, plotY)
end

...
You should probably also add a Unit:JumpToNearestValidPlot() line in there in case the selected plot is occupied, which it should be upon the 2nd barbarian being added to the map, and there's a fair chance the plot will be occupied on the creation of the 1st barbarian.
Code:
for i = 1, numBarbs do
	unit = Players[63]:InitUnit(iUnitType, plotX, plotY)
	unit:JumpToNearestValidPlot()
end
 
I figured I could recycle this old thread.

Three more questions:

1) Pretty similar to another above question; I'd like to check that the player has at least one unit of a particular unitclass, so I'm making a function to count the number of unitclass-type units the player has. It's recycled from JFD's code above:
Code:
function AW_UnitclassCounter(player, unitclassID)
	local numUnits = 0
	for unit in player:Units() do
		if unit:GetUnitClassType() == unitclassID then
			numUnits = numUnits + 1
		end
	end
	return numUnits
end
Except this essentially takes a pPlayer parameter instead of iPlayer, but I don't expect that will break anything. Everything looks alright?

2) Using that function, and recycling some logic from another thread where whoward showed how to choose a random city, can I choose a random unit from that pool of unitclass-type units?
Code:
function AW_RandomUnitFromUnitclass(player, unitclassID)
	local iUnit = math.random(AW_UnitclassCounter(player, unitclassID))
	local i = 0
	for unit in player:Units() do
		if (unit:GetUnitClassType() == unitclassID) then
			i = i + 1
		end
		if (i == iUnit) then
			return unit
		end
	end
	return nil
end

3) Finally, would the condition "if (pPlayer:HasReligionInMostCities(-1)) then" be met if:
a. The majority of pPlayer's cities have no religion, or
b. There is no clear majority religion for pPlayer's empire?​
 
No answer? :/

Another question though:

I want to make a function which will fire once mods are activated, because it will detect if a specific mod is active and if so, add and remove other functions. Given this, what event hook should I use? Events.AfterModsActivate?
 
So, to resurrect this again, is anyone aware of a function that will return what pantheon a player has created? I was surprised to see that there's a Player.GetReligionCreatedByPlayer but not a Player.GetPantheonCreatedByPlayer...
 
Pantheons don't have names - you either have adopted a pantheon belief (and not yet founded a religion) or you haven't

pPlayer:HasCreatedPantheon() will tell you if the player has adopted any pantheon belief, pPlayer:GetBeliefInPantheon() will tell you which one
 
Thread recycled again!

This time, I'm stuck on how to write the algorithm for this: I need to be able to be able to find all cities in the world with Building X , and whether or not Player X has all of them under their control.
So I figure the function could fire on Player X's turn and see if any city in the world has gained Building X. (I can't do it with BuildingCreated or whatever the hook is called, because a) I like to hate on BNW, and b) Building X is never actually built, only added via lua and other indirect methods) Is that even possible to do... without a dummy building? Or will one be required?

And how do I get every city in the world to begin with? :confused:
 
If the building is handed out via Lua to begin with... why not have it update a flag or some other Lua-centric counter at the same time? Then you can just check that particular counter, or a table, and see if there are any that are 'owned' by players that shouldn't have it.

Otherwise, I'd imagine getting every city in the world could be done by looping through all of the players, and doing the pPlayer:Cities() loop inside that.
 
It's... not always handed out through lua. In fact, the greater number of times, it's a building that you receive as one of the benefits of building certain wonders. Just as the Great Library gives a free library, Wonders X, Y and Z give a free Building X. So how that's going to work, I'm not sure. Though I imagine there could likewise be a PlayerDoTurn function for each player that will see if each city has Building X, and if it does, update the lua-centric counter if that hasn't already been done for that city.

I'm thinking... what if there was a table of every city in the world that had Building X, and then for each city a check to see if Player X owns it...? If all of them return true, then the player does indeed own all the cities with Building X?
 
According to whoward CityConstructed should work for all three expansions.
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)

function PlayerTurn(iPlayer)

	-- some code here that adds the building or checks for the building within individual cities, followed by:

	PlayersWithBuildingX[iPlayer] = "true"

	--some more player do turn code

end
GameEvents.PlayerDoTurn.Add(PlayerTurn)

but you would have to persist the lua table PlayersWithBuildingX between saved games.
You can then look within the PlayerDoTurn at the contents of PlayersWithBuildingX as (something like this, though I think it's a little sloppy):
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
So you would have a skeleton code like this:
Spoiler :
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)


function PlayerTurn(iPlayer)

	-- some code here that adds the building or checks for the building within individual cities, followed by:

	PlayersWithBuildingX[iPlayer] = "true"

	--some more player do turn 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

end
GameEvents.PlayerDoTurn.Add(PlayerTurn)
 
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?
 
Back
Top Bottom