Skip to content

Conversation

esbudylin
Copy link
Contributor

This PR continues the work on Terrain Improvements from #710. Previously, all terrain improvements were hard-coded in the game code. This PR moves terrain improvement definitions into the JSON file, which makes them moddable.

This PR also moves the tile yield bonuses into the TerrainImprovement class: previously, they were part of the TerrainType class which allowed defining bonuses only for mine, irrigation, and road improvements. With the new system, a bonus yield of any type can be defined for any terrain improvement.

To fully match the Civ3 bonuses, a special tileModifier field was introduced to the TerrainImprovement class. Similar to the building class (#697), it holds a delegate loaded from a Lua function. Its goal is to allow bonus yields depending on tile context: for the Civ3 ruleset, this is useful for implementing the railroad bonus (only grant additional yield if a mine or irrigation is present on a tile).

Finally (the size of this PR got out of hand!), I made a few changes to the Terraform class. Now terraforms (worker's jobs) can be associated directly with a terrain improvement. Also, the existing logic for terraform effects and prerequisites was moved to Lua.

Right now we have almost all the elements for moddable terrain improvements: it is possible to define new terrain improvements, worker jobs for them, and associate them with custom graphics. The only missing system is the UI for worker jobs: right now the game displays buttons only for a hard-coded list of worker actions. Making it moddable would be a logical step forward.

Copy link
Contributor

@TomWerner TomWerner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a pretty big PR so I probably missed something, but this is super cool!

I stuck this in the discord, but this allows for crazy worker moves like "clear hill"

image image image

public readonly string key;

public readonly Layer layer;
public readonly StrengthBonus? defenseBonus;

// On the game map, overlapping terrain improvement with a higher zIndex cover those with a smaller one
public readonly int zIndex = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this differ from the layer?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

zIndex only affects the drawing order on the game map. The layer affects the gameplay (each tile can have only one type of improvement per layer). Two improvements can belong to the same layer, but have different zIndexes (e.g. irrigation is drawn before roads, and mine is drawn after roads). I added a comment with an explanation to the layer field.


private Dictionary<(TerrainType, Tile.YieldType), int> bonusYields = [];
private readonly SaveTerrainImprovement dataSource;

public TerrainImprovement(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need both constructors?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edit: is one for the hardcoded civ3 rule list?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first constructor is used in EdgeWalkerTest to create a test road instance


local terraforms = {
build_mine = {
validator = function(context)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found it a little confusing at first that we have some tables here with a validator, and some with an effect, but none with both.

I think that's just a naming artifact, right? Would naming these like irrigate_validator/mine_validator/etc work, or would that break things?

If it would break things, it might be worth the duplication to duplicate the clear_foliage validator so the wetlands/forests have a validator and an effect, and then have a comment in mine/irrigate that the default effect is to add the improvement.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this table should be simpler. There's no need for each function to have a sub-table. Fixed it.

local terrain_improvements = {
railroad = {
tile_modifier = function(yield)
local production_improvement = yield.tile.overlays:ImprovementAtLayer(Layer.ResourceDevelopment)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe "yield_improvement"? I don't normally think of irrigation as a production improvement

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, done.

end,
return {
buildings = require "civ3.buildings",
terraforms = require "civ3.terraforms",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth a comment in the Lua file explaining what the difference between a terraform and a terrain improvement is.

Copy link
Contributor Author

@esbudylin esbudylin Jul 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a comment for now, but I think terraform is just a bad name for this type of entity. We should name it something clearer like "worker job".

namespace C7GameData.Save;

public class SaveTerrainImprovement {
public static IEnumerable<SaveTerrainImprovement> Civ3Improvements() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you leave a comment explaining why we need this list of improvements?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@esbudylin
Copy link
Contributor Author

@TomWerner Thanks for the review! I think I've addressed all your comments. I'll leave this PR open for a few days, in case anyone else wants to contribute a review.

@esbudylin esbudylin added Lua anything relating to Lua modding related to scenario data or mod api labels Jul 31, 2025
@esbudylin esbudylin merged commit 73a68b5 into Development Aug 3, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Lua anything relating to Lua modding related to scenario data or mod api
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants