• Civilization 7 has been announced. For more info please check the forum here .

Lua-king for help

Jheral

Prince
Joined
Apr 29, 2009
Messages
502
So I'm working on a little project with Lua, and I have been failing to find the information I need through a more or less thorough search of this subforum and the wiki, and I was hoping someone here could help me out.

  1. First off, I need a reliable way to check the tiles being worked by a city, as well as the ones that are available to it - checking the tiles for yields, resources, improvements and so on.

    I have no idea how to do this as it stands - I couldn't find any information whatsoever. :(

  2. Secondly, I need a way to check whether a process is active in the city (i.e. one of the Wealth or Research style ones).

    Once again, no real info that I could find.

  3. Next up, I need to be able to check the specialists present in the city - count how many there are of each type, and be able to know and call a function when that list changes.

    Yet another place where I am clueless (a lot of those around here :lol: ).

  4. Lastly, I need to call a custom function when a city is founded. This one I found something for, at least, but I don't know how to use it properly. I figure SerialEventCityCreated is the one I want, but how do I pass on a pointer to the founded city into my function?

That would be all for now (although I'm sure to hit more walls while trying to make this work out ;) ). I would very much appreciate it if someone could help me out on these issues.
 
The best place to find answers to these types of questions is in the lua source code.

1) The city view map indicates which tiles are being worked and which tiles can be worked by a city, so the code to do this must be in CityView.lua (C:\Program Files (x86)\Steam\SteamApps\common\sid meier's civilization v\assets\ui\), so you can probably use bits of that for your own tasks.

2) The city view screen also shows what a city is doing - building, research, wealth - so the code to check for this will also be in CityView.lua

3) Again, the city view screen (under the built list) includes what specialist slots are available and which are being used, so the code to check for that is also in CityView.lua - counting how many there are is probably easier than detecting when the list changes

Looks like you need to "read" CityView.lua for answers to the first three questions. :)

4) Get yourself a good file search tool (I use Agent Ransack) and search the game lua files for that event - you'll find three examples in CityBannermanager.lua, CityList.lua and UnitFlagManager.lua

Digging around in CityBannerManager you'll find

Code:
function OnCityCreated( hexPos, playerID, cityID, cultureType, eraType, continent, populationSize, size, fowState )
...
end
Events.SerialEventCityCreated.Add( OnCityCreated )

which gets you the playerID and the cityID of the new city

dig around a bit more and you'll find
Code:
local player = Players[playerID];
local city = player:GetCityByID(cityID);

which you can use to get a "pointer" to the new city

You need to be careful with the "SerialEvent"s, as they don't always trigger for the AI, so you'll need to do some testing.

HTH

W
 
CityView does indeed look like it has what I need. I just have to read through it... (sigh)

Thanks for the help. :)
 
Right. I've found answers to the three first issues (I think, anyway).
  1. Cycle through plots worked by the city:
    Spoiler :
    Code:
    		for i = 0, pCity:GetNumCityPlots() - 1, 1 do
    			local pPlot = pCity:GetCityIndexPlot( i );
    			if ( pPlot ~= nil ) then
    				if ( pPlot:GetOwner() == pCity:GetOwner() ) then
    					if ( ( i == 0 ) or pCity:IsWorkingPlot( pPlot ) ) then 
    					end
    				end
    			end
    		end
  2. Check if a process is being worked on:
    Spoiler :
    Code:
    		for process in GameInfo.Processes()
    			if ( process == GameInfo.Processes[pCity:GetProductionProcess()] ) then
    			end
    		end
  3. Check the specialists active in the city:
    Spoiler :
    Code:
    		for specialist in GameInfo.Specialists()	
    			local specialistID = specialist.ID;
    			if ( pCity:GetSpecialistCount( specialistID ) ~= 0 ) then
    			end
    		end

Still struggling with the issue of updating, though - I'd need it to update whenever anything that affects the city changes; buildings, techs, policies, specialists, worked tiles etc...

Anyone with a clue about how to do that in a good way?
 
I'd need it to update whenever anything that affects the city changes; buildings, techs, policies, specialists, worked tiles etc...

For most of those, you can't. There's no event that triggers when a building is completed, and while there IS an event that triggers when a tech is gained (Events.TechAcquired), it doesn't actually report which player or which tech was gained, just the fact that someone gained some tech somewhere.

Most of the existing Lua functions for these are designed to either trigger once per turn, or whenever a certain UI element is active. Many have limitations that are not an issue for the way the core game uses them, but that would be crippling if you wanted to use them for something more general. For instance, there's an event that triggers when an Improvement is created, but it'll only trigger if the active player (you) can SEE the hex in question; this is fine for most UI purposes, but it means that you can't use that event for something more general (like terraforming).

Take that TechAcquired event I mentioned: it's used by the Victory Progress screen, for the bit where it tints part of the Apollo Program's picture to show how close you are to reaching that tech. So whenever a tech is gained, the VictoryProgress.lua function counts up all of the techs for the active player to see what fraction to plot. It's very wasteful, since it'll repeat the logic even if the active player wasn't the one who gained the tech, but it works. But if you wanted to do something similar in a more active bit of UI code, then you'll have a real problem. And if you want the logic to actually care about which tech was the one gained, then you'll have to find some way of storing an array of which techs each player had, and checking against that list to see what new tech has been added.
 
There's no event that triggers when a building is completed,

Events.SpecificCityInfoDirty will tell you when something changed in a city, which may be enough, depending on what you want to do.

and while there IS an event that triggers when a tech is gained (Events.TechAcquired), it doesn't actually report which player or which tech was gained, just the fact that someone gained some tech somewhere.

Use GameEvents.TeamTechResearched instead.


For policies you can use GameEvents.PlayerAdoptPolicyBranch and GameEvents.PlayerAdoptPolicy
 
Events.SpecificCityInfoDirty will tell you when something changed in a city, which may be enough, depending on what you want to do.

I thought that one was limited to the active city/player; which as you say, may be fine depending on what you're doing. I've generally avoided those sorts of events since most of my Lua logic needs to work for the AI players as well as the human, but that might not matter to you.

Use GameEvents.TeamTechResearched instead.

Forgot about that; yes, they've added a GameEvent for research, which is much better than the old serial event. (It's strange, actually: they added that GameEvent, but didn't bother to go back and update the UI code to use it instead of its more maintenance-intensive ancestor?)

The only thing is, I don't know if either option triggers if a tech is awarded through some means other than research. In the core game you can gain techs through other means (a Great Scientist or certain Wonders), and those may or may not work with that event.

But some mods, such as my own, award certain techs through Lua commands, and I'm pretty sure those DON'T trigger that event. Obviously this isn't a problem if you're modding the core game, but if you want to create some sort of complex scenario, this could cause problems. Although again, whether this is an issue depends on what you'd use the information for.

For policies you can use GameEvents.PlayerAdoptPolicyBranch and GameEvents.PlayerAdoptPolicy

Same caveat as above. The core game has a couple ways other than adoption to gain a Policy (like some Wonders), but some mods (again, such as my own) award certain policies to players behind the scenes as a way of providing changing empire-wide bonuses. This may cause issues.
 
Events.SpecificCityInfoDirty will tell you when something changed in a city, which may be enough, depending on what you want to do.
I thought that one was limited to the active city/player; which as you say, may be fine depending on what you're doing. I've generally avoided those sorts of events since most of my Lua logic needs to work for the AI players as well as the human, but that might not matter to you.

Unfortunately, I do need it to work for AI as well as for the human player, so yeah - it might be an issue. I will have a look at that function, though. If it does work for AI as well, it would be ideal for what I'm trying to do. Especially since I don't actually need to know the specifics about what has changed.

Use GameEvents.TeamTechResearched instead.

For policies you can use GameEvents.PlayerAdoptPolicyBranch and GameEvents.PlayerAdoptPolicy
But some mods, such as my own, award certain techs through Lua commands, and I'm pretty sure those DON'T trigger that event. Obviously this isn't a problem if you're modding the core game, but if you want to create some sort of complex scenario, this could cause problems. Although again, whether this is an issue depends on what you'd use the information for.

Same caveat as above. The core game has a couple ways other than adoption to gain a Policy (like some Wonders), but some mods (again, such as my own) award certain policies to players behind the scenes as a way of providing changing empire-wide bonuses. This may cause issues.

I will very likely be awarding both policies and techs (and other... things :mischief:) through Lua commands. On the other hand though, if I'm adding new commands, I can make those commands be taken into account, so I think I can live with it. ;)

And I suppose, as much as I might dislike it as an option, I could make it just check on turn start, if all else fails.
 
I will very likely be awarding both policies and techs (and other... things :mischief:) through Lua commands.

I should warn you, then... awarding free techs only has a few minor issues, mainly that Barbarians and City-States can "steal" techs once a player has it, so you can't use hidden techs to provide bonuses unless you find some way to stop a barbarian or city-state from doing this. (Really, the best way is just to check the barbs each turn and if they have this tech, remove it.)

But awarding policies can break far more things. If you're just awarding existing policies in branches the player has already opened, then you'll have a minor problem where the cost of the next policy will go up. That is, if the first policy costs 20 culture and the second 40, then awarding the player a policy on turn 1 makes his first policy cost 40. My own mods use a hidden policy to provide a bunch of modifiers (so that at some point in the game I can remove the bonuses), and so this was a major issue for me. In the end, I had to recalculate the cost equation to give roughly the same progression but with N+1 instead of N. And I never actually remove the hidden policy, I swap it for one that has no bonuses/penalties.
The real problems come if you're trying to create an entirely new policy, like the one I mentioned to provide bonuses/penalties in my mod. Depending on where you place it, you'll have UI problems, problems completing a Cultural Victory, and/or massive AI issues where it gets stuck. It took me a LONG time to design a workaround for all of these issues.
 
Thanks for the advice, though I'm guessing we'd have to have a look at that whole system anyway - we've got some fairly major changes to the policy system in general planned (some of which would invalidate Cultural victory altogether, so that shouldn't be an issue at least :mischief: ).

I guess we'll see how it plays out.
 
Back, after a short hiatus, and I have yet another issue I could use help with:

Spoiler :


The problem here is that, after trying to modify the cityView screen, the cityView is always visible, even when outside cities. I have so far failed to figure out how to correct this, as I cannot see any problem with the code I've added. Despite not seeing any errors, I suspect it's to do with the additions to the CityView.lua, since the tooltip I tried to add does not appear at all. But since I don't get any error messages, I haven't a clue as to what exactly is wrong.

I would very much appreciate it if someone could help me out.
 
CityView.lua is replacing an existing core file so should only be VFS=true and not an InGameUIAddin in your own mod
 
So in other words just me being stupid and misunderstanding the tutorials again. :p

How lovely. I will try that.

Edit: Indeed. That worked nicely. Now I just need to figure out the tooltip issue, and I'm set.

Thanks for pointing that out to me.
 
Ok, including prints in the tooltip function tells me that it does get into it, and that it does register what it should be.

...and yet, for some reason, it never gets applied.

Edit: Tooltip issue resolved. Yet another basic mistake - I'd defined the string variable that was to get returned by the function inside an if-statement, so it didn't last until the return.
 
Once again, I have encountered problems that cannot be resolved with the information I have at hand (thank you, firaxis, for being so open and generous towards your mod community).

I have been trying to apply a yield to a city, to change it's total output of that yield to fit with a model determined through a script of my own making. Unfortunately, the only way I found was to modify the city tile's yields, using Game.SetPlotExtraYield. This function cannot set a tile's yields to be lower than 0, and as such it cannot be used to counter yield discrepancies that are bigger than the city tile's base yield. In other words, it's not good enough, and I need another way.

Is there some way I could directly modify the city's yields? Or perhaps I could use a dummy building to hold the yield offsets, and modify it's yields as the game progresses?

If so, how would I go about it?
 
Is there some way I could directly modify the city's yields? Or perhaps I could use a dummy building to hold the yield offsets, and modify it's yields as the game progresses?

You can't alter a building's yields as the game progresses. What you CAN do is what I did in my own mod: make a building that gives +1 production, a building that gives +1 food, a building that gives +1 Happiness, etc., and use SetNumRealBuildings to place as many copies of each in a city as you need. Just make sure to have that building's class definition use the NoLimit flag.
As long as you've modded CityView to hide any building with NoLimit, you can have as many of these as you need (or none) and it won't show up in the player's UI... except that the city totals for each yield will have a different Buildings total than what they see. My own mods have done this for a long time, and it's one of the bits the patch/expansion didn't break.

While you can't put a negative number for SetNumRealBuildings, I believe you CAN make a new building with a -1 yield; I haven't tried it, because my needs were all for positive values (except for Happiness, and I KNOW you can't make a -1 building for that without leveraging a Policy). If you can't make a -1 in the building table, you can definitely give negative values through a Policy, but that's a whole other can of worms.
So assuming the negative yield works, you'd just create one building for a +1 and one for a -1, then use a simple IF check to see which should be in the city for that turn and how many are needed.
 
Interesting. I had forgotten that that tag was there. It would definately cover everything I need to do with this, although I am a bit uncertain about how to not show NoLimit buildings in the CityView.

How would you suggest doing that, and would it perhaps be worthwile to add a bInvisilble tag to be used for that instead?
 
Amusingly, that idea fell flat - I have been using the SpecificCityInfoDirty event to trigger a recalculation of the values, and it turns out that SetNumRealBuilding activates that event, causing an infinite loop. This could be problematic.

I suppose another solution will have to be found.

Maybe if I prevent it from being run for the same player/city for a number of ticks after it runs? Would that be possible?
 
Maybe if I prevent it from being run for the same player/city for a number of ticks after it runs? Would that be possible?

Not so much time-based, but you can have it toggle a flag each time the event activates. If the flag is false, do the effect and set the flag to true, then at the end of the turn set it back again. This ensures that it only triggers once per turn, no matter what else you do. (I did something similar back when I was trying to create a promotion to allow a unit to move over both land and sea; I'd tried to override the embarkation through this sort of thing. In the end, I found a better way that didn't require any Lua at all.) These sorts of flags are useful for all sorts of things; in my own mod, I added a Rookie promotion that's given to all new units, which goes away after their first fight, as a way to prevent SerialEventUnitCreated from triggering twice if the unit decided to embark on its first turn.

But honestly, I'd say that SpecificCityInfoDirty is just the wrong event to be using in general. It's a UI display event, not the sort of thing you should be changing values within; an AI player doesn't open UI windows, so would it be getting the same bonuses/penalties you're giving to the human? This is why nearly all of my hidden buildings are handled through PlayerDoTurn, a GameEvent which triggers once per turn, and a few others are handled through more specific triggers (like slotting/unslotting a specialist, saving the game, and so on). Very few things need to be adjusted more often than once per turn, in my experience. Even if you want it to be more reactive than that, there's still probably a better choice, but I'm at work and can't tell you what other options there are.
 
I have it set up to run the checks on PlayerDoTurn as well, but I need it to check continously for the player, since a PlayerDoTurn won't let the values update if you change anything about the city during the turn, which would make the information available in the CityView not update to reflect those changes. That also means that the "once per turn" approach would kind of defeat the purpose of the whole thing. Cities, after all, are not units - they don't take actions like they do, and have no promotions that can be lost after things change.

Replacing the SpecificCityInfoDirty might not be a bad idea, but I haven't been able to find event hooks to cover enough stuff to do that. SpecificCityInfoDirty runs whenever something changes about the city - except, seemingly, for changing the tiles that are being worked - at least for the player.

Now, if I could find a good list of events (that is clear enough to be read and understood) that could cover any such change - city founding, capture and destruction; worked tiles changed; building added; production changes; specialist changes; tech changes; policy changes; changes to any tile within the city's workable radius etc... - then I could use that instead. But unless I have that, I might as well use this one, since it does do most of what I need it to do.
 
Top Bottom