Question: Any way to add a build or mission to change terrain?

Gedrin

Chieftain
Joined
Mar 27, 2008
Messages
52
So Workers have a "build" that can remove a feature [Forest, Jungle, Marsh]
But is there any way to change the underlying terrain from say some flatlands to coast?
 
Yes, you can do that. Not in XML, of course; the only things you can do through simple XML tables are the things the devs have already put functions in for. Obviously, this isn't one of those things.

You can do it in Lua. In fact, I HAVE done it in Lua, in my mod (see .sig), almost a year ago. The way you do it is this:
> Create a new Improvement type, named, for instance, "Terraform". Other ones I've added: "Plant a Forest", "Plant a Jungle", "Raise/Lower Hills", and "Create a Deep Mine". (The name of the Improvement should be the name of the Build action.)
> Create a Build action to place that Improvement.
> Give the Improvement a moderate yield, something big enough that the AI will see it as superior to a Farm/Mine/Trading Post, but not so large that the AI would want to do it again as soon as you terraform. (Terraforming in a specific direction isn't so bad, but if you make any kind of reversible chain, like Plains -> Grassland -> Plains, then this is a real danger.)
> Set the terrain types that the Improvement can be built on. The problem with changing anything to a coast is, what happens to the Worker when the solid ground he was standing on suddenly becomes water? So in my mod, I have it go Snow -> Tundra -> Plains and Desert -> Grassland. (Hills are not a Terrain type, they're a Plot type, hence the separate Raise/Lower action for those.) As long as the list of valid terrain types doesn't include Plains or Grassland, then the AI can't get stuck in an infinite loop.
> Create a start-of-turn Lua Event (preferably the GameEvent version) that checks if you have any instances of the Improvement in question (there's a Player:CountNumBuildings command). If so, then scan the map to find it; once you do, remove the Improvement and change the Terrain type on that hex through a Lua command.

Ta da. It works; like I said, I've had this in my Alpha Centauri mod for about a year now.

There are a few significant problems with this:
1> The UI doesn't refresh terrain graphics when you change a hex's terrain, because the Civ5 game engine blends the graphics for each hex with the six around it to make a smoother-looking terrain. To see the changed terrain, you'd have to save your game, exit to the main menu, then reload it. This drawback does not apply to Feature changes (planting a forest or jungle), but does apply to Plot changes (adding/removing a hill or mountain, or changing water into land). It also doesn't apply to Resource changes (my Deep Mine action creates a new instance of various resources), which'll appear immediately.
2> If you want to terraform impassable terrains (mountains, lakes, oceans), then you have to find a way to get the worker unit TO that hex. Mountains and lakes are easy enough, just give the "Hovering Unit" promotion to a worker unit. (Yes, Lakes are not actually treated as water. They're a land hex with the impassable flag and a special graphical rule.) But for ocean, you'd need to either create a sea unit to perform the action (like the Work Boat) or create a unit that can move across both land and sea (and yes, it's possible; I've got several of those units in my mod, including my Former unit designed to do exactly this). A normal worker can't stand on a water hex without embarking, which disables all build actions.
3> The AI can use these actions, but not very well, especially if it's something requiring a specific hex to work in. The AI can do basic yield analysis, but won't see the long-term implications. This is especially problematic for any sea terraforming; a city will only produce a work boat if it believes there's a local resource for the unit to improve (and the AI will NOT connect sea-based strategics, like Oil, or any other new sea resources you decide to add. Yes, the vanilla AI is completely broken in this way.) So if your desire to terraform the coast involves creating a disposable workboat-style unit to do the action, the AI simply won't build one since it won't think it has a need for one.
4> It requires an Improvement, which means that terraforming will destroy whatever improvement is on the hex already. While this is generally a no-brainer (since you can make a new improvement on the newly-altered terrain), you have to make sure the yield isn't so high that the AI thinks it should replace that Academy or Manufactory it just placed, or disconnect that strategic resource it harvested.
5> You can end up with some broken combinations if you're not careful. Flood Plains are a feature, and are decent on a Desert hex (2 food and +1 gold for the river), but if you terraform that desert into a grassland without removing the flood plains, suddenly that hex is 4 food and 1 gold. Or you might terraform a hex containing a resource into a terrain type that can't support the Improvement needed to harvest that resource. (Especially problematic with your land->water idea.) Even though the resource Improvement table overrides the general terrain limit tables, the game won't have usable graphics for certain combinations, and you still have to get a worker with the right build action to that hex; Work Boats don't know how to create Mines, Farms, etc. to harvest land-based resources that end up in their territory. (Easy enough to fix if you really care, though.)

There are a few other minor issues with this method, but this should get you started.
 
Wow thanks for a fantastic response...

Yes, you can do that. Not in XML, of course; the only things you can do through simple XML tables are the things the devs have already put functions in for. Obviously, this isn't one of those things.

Yeah I was looking for some lua event that handled worker build completed or mission button clicked... and s'ok... this would not be my first time with a mod :D

quote trimmed for brevity
> Create a new Improvement type, named, for instance, "Terraform".
> Create a Build action to place that Improvement.
> Give the Improvement a moderate yield, something big enough that the AI will see it as superior to a Farm/Mine/Trading Post,
> Set the terrain types that the Improvement can be built on. The problem with changing anything to a coast is, what happens to the Worker when the solid ground he was standing on suddenly becomes water?
> Create a start-of-turn Lua Event (preferably the GameEvent version) that checks if you have any instances of the Improvement in question (there's a Player:CountNumBuildings command).

My concern about the value for the AI is that this is [obviously I think] for an implementation of canals. Since with unit's not needing a transport a 1 tile wide section of coast really just slows land units down... it also breaks trade routes but I expect a canal makes more sense when it connected to a city [which would bridge that canal]... but if the AI just sees it as a possible improvement I don't want them to go and wipe out all their land mass because they think the "canal" improvement is "productively" useful. It should have a strategic value only... which of course will likely mean the AI ignores it [due to their poor assessment of long term value of something].

As for the worker suddenly on water... I figured
1) It will automatically embark but if not
2) It can be programatically set to embarked but if not
3) I'm considering deleting the unit to represent a way higher cost to remove terrain... which is also why I was considering a mission not a build. Trouble is I see no lua events related to mission completion and I bet those are in the DLL.

WRT Create a start-of-turn Lua Event
Shame that it comes down to lua scanning the whole map every turn... sounds slow.
[Hmmm... although it would be faster to loop over every worker and see if they are standing on some improvement "x"... since the turn they finish it is the only time the will be able to be standing on it... whatever, implementation detail]


quote trimmed for brevity
There are a few significant problems with this:
1> The UI doesn't refresh terrain graphics when you change a hex's terrain...
2> If you want to terraform impassable terrains...
3> The AI can use these actions, but not very well...
4> It requires an Improvement, which means that terraforming will destroy whatever improvement is on the hex already....
5> You can end up with some broken combinations if you're not careful....

There are a few other minor issues with this method, but this should get you started.

1) Meh I can live with that.
2) Nope :) Flatlands only... not even hills
3) This is my main concern... first pass I plan to make it seem worthless so the AI wont.
4) Planned that already... also all features and resources which means:
5) not so much of an issue.

Random musings:
Maybe I can find some way to ensure that to build a canal the worker must be
1) in your territory
2) beside exactly one tile containing either a city or "canal".
This way canals must start at a city and cannot get out of control or break traderoutes.


Anyway this is a great start and thanks again
 
Yeah I was looking for some lua event that handled worker build completed or mission button clicked...

The closest is SerialEventImprovementCreated, but it has one HUGE drawback: it only triggers if you, the active user, can SEE the hex in question. The original version of my terraforming used this mechanism; I'd place the improvement, and the game would immediately replace it with the altered terrain/features... except that it wouldn't do so in AI territory, which obviously hurt the AI. Hence the shift to a simple end-of-turn accounting.

but if the AI just sees it as a possible improvement I don't want them to go and wipe out all their land mass because they think the "canal" improvement is "productively" useful.

This is the biggest single problem with the current Flavor system. There's really no easy way to implement a "do this a few times but not EVERY time" criteria. If you make something desirable, then it'll try to do it (or at least consider trying to do it) in every applicable situation.

A related issue prevents cities from really specializing. You, the human, understand that half-measures aren't very good; if you have six cities, you're best off placing the entire XP-generation chain (Barracks, Armory, Military Academy, etc.) in one or two cities and not building any of them in any others, and then producing all of your military units from those one or two cities only.
The AI, on the other hand, just can't do that sort of logic. If a Barracks is desirable, then every city will get one. If the Armory is even more desirable, then everyone will build that one too. And not only will the AI only build units in the cities with the most buildings, but they won't even wait for those buildings to be built before making the units; the AI might build the units and THEN the Military Academy, while the human knows to do those in the right order.

So yes, this is a real problem for terraforming. It really only works well for no-brainers (turning desert into grassland, for instance) where you WOULD want to do it wherever possible.

Now, there may be ways to artificially block these sorts of things, through the GameEvents, but we're still learning those.

Shame that it comes down to lua scanning the whole map every turn... sounds slow.

Not what I said. Here's the relevant bit of code, in its entirety:
Spoiler :
Code:
function SpatzTerraform()
	local tCount = 0;
	for index,pPlayer in pairs(Players) do
		if( pPlayer:GetImprovementCount(GameInfoTypes.IMPROVEMENT_PLANT_FOREST) > 0) then
			tCount = tCount + 1;	
		end
		if( pPlayer:GetImprovementCount(GameInfoTypes.IMPROVEMENT_PLANT_JUNGLE) > 0) then
			tCount = tCount + 1;	
		end
		if( pPlayer:GetImprovementCount(GameInfoTypes.IMPROVEMENT_RAISE_LOWER_HILLS) > 0) then
			tCount = tCount + 1;	
		end
		if( pPlayer:GetImprovementCount(GameInfoTypes.IMPROVEMENT_DEEP_MINE) > 0) then
			tCount = tCount + 1;	
		end
		if( pPlayer:GetImprovementCount(GameInfoTypes.IMPROVEMENT_TERRAFORM) > 0) then
			tCount = tCount + 1;	
		end
		if( pPlayer:GetImprovementCount(GameInfoTypes.IMPROVEMENT_MONOLITH_TEMP) > 0) then
			tCount = tCount + 1;	
		end
	end

	if ( tCount > 0 ) then
--print("Starting Terraform check!");
		local GridX, GridY = Map.GetGridSize();
		for iX = 0,GridX do
			for iY = 0,GridX do
				local plot = Map.GetPlot(iX, iY);
				if ( plot ~= nil ) then
					if ( plot:IsOwned() and not plot:IsWater() ) then
						local imptype = plot:GetImprovementType();
						if( imptype ~= -1 ) then
-- Technically, we don't need this check if we've already checked ImprovementState.
							local impname = GameInfo.Improvements[imptype].Type;
--print("  Improvement type: ",impname,imptype, "; x,y = ",iX, iY);
							if(impname == "IMPROVEMENT_MONOLITH_TEMP") then
								plot:SetImprovementType(GameInfoTypes.IMPROVEMENT_MONOLITH);
								plot:SetFeatureType(GameInfoTypes.FEATURE_MONOLITH,-1);
--								plot:ResetFeatureModel();
--								plot:UpdateVisibility();
							end
							if(impname == "IMPROVEMENT_PLANT_FOREST") then
								plot:SetImprovementType(-1);
								plot:SetFeatureType(FeatureTypes.FEATURE_FOREST,-1);
--								plot:ResetFeatureModel();
--								plot:UpdateVisibility();
							end
							if(impname == "IMPROVEMENT_PLANT_JUNGLE") then
								plot:SetImprovementType(-1);
								plot:SetFeatureType(FeatureTypes.FEATURE_JUNGLE,-1);
							end
							if(impname == "IMPROVEMENT_RAISE_LOWER_HILLS") then
--								local terrtype = plot:GetTerrainType();
								plot:SetImprovementType(-1);
--								note: the two flags on each are recalc of areas, and rebuild of graphics
								if plot:GetPlotType() == PlotTypes.PLOT_HILLS then
									plot:SetPlotType(PlotTypes.PLOT_LAND, true, true);
								else
									plot:SetPlotType(PlotTypes.PLOT_HILLS, true, true);
								end
							end
							if(impname == "IMPROVEMENT_TERRAFORM") then
								local terrtype = plot:GetTerrainType();
								plot:SetImprovementType(-1);
								if(terrtype == TerrainTypes.TERRAIN_DESERT) then
									plot:SetTerrainType(TerrainTypes.TERRAIN_GRASS, true, true);
								elseif(terrtype == TerrainTypes.TERRAIN_TUNDRA) then
									plot:SetTerrainType(TerrainTypes.TERRAIN_PLAINS, true, true);
								elseif(terrtype == TerrainTypes.TERRAIN_SNOW) then
									plot:SetTerrainType(TerrainTypes.TERRAIN_TUNDRA, true, true);
								else
								end
--								ReloadTextures(0,0);
--								ToggleStrategicView();
							end
							if(impname == "IMPROVEMENT_DEEP_MINE") then
								plot:SetImprovementType(-1);
								local restype = "";
								local res_amt = 1;
								local diceroll = 0;
--								print( "Deep mine: ",diceroll );
								if ( GameInfoTypes.RESOURCE_DILITHIUM ~= nil ) then
									diceroll = Map.Rand(12,"Selection of deep resource");
								else
									diceroll = Map.Rand(7,"Selection of deep resource");
									-- If, somehow, you allow Deep Mining in earlier eras, it can't extract Dilithium or Neutronium.
								end
								if (diceroll == 0) then
									restype = "RESOURCE_ALUMINUM";
									res_amt = 6;
								elseif (diceroll == 1) then
									restype = "RESOURCE_URANIUM";
									res_amt = 3;
								elseif (diceroll == 2) then
									restype = "RESOURCE_COAL";
									res_amt = 5;
								elseif (diceroll == 3) then
									restype = "RESOURCE_OIL";
									res_amt = 3;
								elseif (diceroll == 4) then
									restype = "RESOURCE_GOLD";
									res_amt = 1;
								elseif (diceroll == 5) then
									restype = "RESOURCE_SILVER";
									res_amt = 1;
								elseif (diceroll == 6) then
									restype = "RESOURCE_GEMS";
									res_amt = 1;
								elseif (diceroll < 10) then
									restype = "RESOURCE_DILITHIUM";
									res_amt = 5;
								else
									restype = "RESOURCE_NEUTRONIUM";
									res_amt = 4;
								end
								plot:SetResourceType(GameInfo.Resources[restype].ID, res_amt);
--								for resource in GameInfo.Resources() do
--									if (resource.Type == restype) then
--										plot:SetResourceType(resource.ID, res_amt);
--									end
--								end
							end -- Which type of improvement is it?
						end -- if there's an improvement on the plot
					end -- if it's an owned plot, and not water.
				end -- if the plot exists
			end -- Y loop
		end -- X loop
	end -- is there even anything to check
end
Events.ActivePlayerTurnEnd.Add(SpatzTerraform);

What happens is it says "are there any instances of IMPROVEMENT_TERRAFORM on the map?". This check is very quick. Since that improvement will only exist for a single turn, the chances of there being one on the map in any given turn is small. So the full map-scan is ONLY done on those turns when the improvement actually exists, and since the improvement is immediately destroyed in the terraforming action, it won't trigger the following turn.
Since the terraforming actions come towards the end of the game, you won't have any time impact at all in earlier eras.

[Hmmm... although it would be faster to loop over every worker and see if they are standing on some improvement "x"... since the turn they finish it is the only time the will be able to be standing on it... whatever, implementation detail]

That method has a few drawbacks:
1> Improvements created by killing a unit (anything involving the workboat, or the Great Person-created improvements). In my mod, the Monolith is the improvement created by sacrificing a Great Empath (spawned by a new specialist type, which adds 1 Happiness and 2 Food). The Great Empath can place the Monolith, which changes the tile's yields to 2 food, 2 gold, and 2 production, and then adds 3 Happiness. To do this, it places a Feature on the hex (since improvements can't add happiness).
2> It IS possible for an improvement to be created without a unit there at all, through Lua. For instance, you might add the "Temple of Gaia" building in a city, which causes inhospitable nearby tiles to spontaneously improve themselves or forests to spontaneously spawn. Granted, if you're using Lua already then there's little reason to mess around with intermediate improvements, but there are drawbacks to an all-Lua method.

Meh I can live with that.

This'll be your mantra for all of modding, along with "It's all a game, I should really just relax." When it comes down to it, would you really CARE about not having that newly-terraformed Desert look like a grasslands? It's not like it'd screw up your ability to move units around, and most people let the AI manage their cities' tile selections anyway, so it's not much of a practical drawback.

The AI issue is far more significant. If you're changing things in ways the AI can't easily account for, then it gives a huge edge to the player. And it's not like the players need any more help to win; vanilla Civ is way too easy as it is. So you just have to be very careful when adding this sort of thing; if whatever you add includes some sort of drawback, then the AI can easily cripple itself, and if you don't then the AI can be at an even larger disadvantage relative to the player. If you only allow this sort of thing near the endgame then it's not so bad, but if it's going to be an option all game long then it's not so good.
 
Back
Top Bottom