[LUA] Force a tile to be worked outside city radius

3335d

CCtP Player
Joined
Jun 15, 2012
Messages
642
I had created a thread earlier here regarding an expansion of the city radius. The city radius values found in the game databases are actually hardcoded, and hence there is nothing that we can do about this until the DLL is released "in a few months" (or whenever). However, it was hinted to me that one could force a plot outside of the city radius to be worked by means of LUA. I would like to know how that could be done and how to create a UI so that the human player will be able to assign their citizens to work tiles beyond the city radius.
 
I haven't seen any references to this, but I don't think it is too far fetched of an idea. It's possible that it is allowed by dll (maybe without any radius limit at all), but prevented by UI (for human) and some restriction on the AI in the AI's own code. Or not. There is no way to know except to try.

I think that Network.SendDoTask() using TaskTypes.TASK_CHANGE_WORKING_PLOT might be what you need. Maybe not though -- I'm not sure whether that function changes what plots are worked or if it changes the "city work override" plots. I haven't tried it and, as far as I know, no one else has either. So you are in for a lot of trial-and-error. See if you can force a city to work inside the 3-tile radius first, of course. Then try further.

You would have to create your own UI and AI for this, of course. Quite a few months of effort even if it does work.
 
Thanks Pazyryk. I'm rather new to Lua, so if it doesn't pain you too much could you please write a function (no UI necessary at the moment) which might force a given tile to be worked beyond the city radius?
 
I don't know how to use this function (if it works at all) so you are going to have to drive this problem yourself a bit.

What you should do is make sure you know how to try stuff in Fire Tuner. The first thing to do is a simple print("hello") statement. Then try something more advanced like UI.GetHeadSelectedUnit():SetExperience(100).

Then search all game assets (using Windows Grep or similar) for "TASK_CHANGE_WORKING_PLOT" and then (if that fails, which is likely) just "Network.". Network methods are unfortunately one of the more mysterious parts of Lua modding. All we can do is make guesses based on examples from the base game. Post what you find here and we might be able to puzzle it out.
 
I think I've hit gold.

From CityView.lua:

Code:
function OnSlackersSelected()
	local pCity = UI.GetHeadSelectedCity();
	if pCity ~= nil then
		Network.SendDoTask(pCity:GetID(), TaskTypes.TASK_CHANGE_WORKING_PLOT, 0, -1, false, bAlt, bShift, bCtrl);
	end
end

I think this is exactly what we need (also from CityView.lua). What I'm assuming is that where iPlotIndex == 0, it is the city tile; anything else appears to be fair game. Only the UI is lacking:

Code:
function PlotButtonClicked( iPlotIndex )
	if iPlotIndex > 0 then
		local pCity = UI.GetHeadSelectedCity();
		Network.SendDoTask(pCity:GetID(), TaskTypes.TASK_CHANGE_WORKING_PLOT, iPlotIndex, -1, false, bAlt, bShift, bCtrl);
	end
end
 
It's kind of weird because iPlotIndex usually refers to the "whole map" plot index, which goes from 0 to number plots minus 1. So plot index 0 is a real and valid plot. But it looks like iPlotIndex == 0 has some other meaning here. I thought at first that it might be an "offset" from city center (really iPlot - iCityCenterPlot). But that doesn't make sense because then you would have negative values, which doesn't seem right given the second function ("if iPlotIndex > 0 then...").

I'm a bit stumped looking at this, since the method names aren't telling me what they really do. I'll take a look at this in the file to see if I can understand what these functions are doing.

Edit:
If you want to try this in Fire Tuner, just open a city view and then type:

pCity = UI.GetHeadSelectedCity()
print(pCity) --this prints a hex number (if it doesn't then you didn't have a city selected)

Then type the Network lines as you see above, but you will need values in place of the last 3 args (probably false although sometimes it wants a 0 instead). I'm still perplexed by their use of iPlotIndex, so your guess is as good as mine for that. If you master this method, then you should be able to change workers at will while you are watching in the city view. Once you know this works, try going out to 4 radius (which you can still see in the city view, though I can't say whether the UI will render it correctly even if the worker does work that tile).
 
I cannot express my thanks to you, Pazyryk, for helping me with this important game mechanic that I would like to implement. I will do the FireTuner once I can access a Windows computer (I am currently on a Mac without a Windows partition :( ).
 
Don't get too confident about this. Just because the method exists and you can supply a plot doesn't mean that the game engine will accept any plot. You'll still have to try it to know.
 
Thinking about this some more -- and I now believe it won't work. The problem is that "cityPlotIndex" only goes out to 3 radius. You can see that if you run this code (from Gedemon in another thread):
Code:
include( "FLuaVector" )
function ShowCityPlots(hexX, hexY)
	local mouseOverPlot = Map.GetPlot( hexX, hexY );
	if (mouseOverPlot ~= nil and mouseOverPlot:IsCity() ) then
		local mouseOverCity = mouseOverPlot:GetPlotCity()
		for i = 0, mouseOverCity:GetNumCityPlots() - 1, 1 do
			local plot = mouseOverCity:GetCityIndexPlot( i )
			Events.SerialEventHexHighlight( ToHexFromGrid( Vector2( plot:GetX(), plot:GetY() ) ), true, Vector4( 1.0, 1.0, 1.0, 1 ) )
			local city = plot:GetPlotCity()
			print (city)
		end
	else
		Events.ClearHexHighlights()
	end
end
Events.SerialEventMouseOverHex.Add( ShowCityPlots )

This highlights all of the city's "cityPlotIndexes". Unfortunately, these are invariantly all plots out to 3 radius, regardless of actual ownership. Doesn't matter if a city owns plots out to 4 radius: its cityPlotIndexes still only go out to radius 3.

Based on the Network functions above treating iPlotIndex == 0 as special, I'm assuming that it really takes cityPlotIndex rather than mapPlotIndex. Since these indexes only go out to 3 radius, it seems very unlikely that you can supply some index outside this range and have it mean anything.

Of course, there is a tiny possibility that city:GetNumCityPlots() is being "dishonest", and that you can get a valid plot from plot = city:GetCityIndexPlot( i ), where i >= city:GetNumCityPlots(). But I doubt it.
 
If the method expects cityPlotIndex (which I'm 95% sure is correct) then it will try to interpret any integer in that way. It doesn't matter if the integer happens to be a map index or a unit index or your grandmother's age. It will still interpret it based on the way the method is coded (and that code is in the dll).
 
Back
Top Bottom