From 37c09f53786e199b865d67cecfa25101893dc259 Mon Sep 17 00:00:00 2001 From: Antti Halme Date: Wed, 18 Feb 2026 23:56:32 +0000 Subject: [PATCH 1/3] Add river delta textures --- C7/Lua/texture_configs/civ3/terrain.lua | 2 + C7/MapView.cs | 79 ++++++++++++++++++------- 2 files changed, 60 insertions(+), 21 deletions(-) diff --git a/C7/Lua/texture_configs/civ3/terrain.lua b/C7/Lua/texture_configs/civ3/terrain.lua index 4802b8743..5c43e655b 100644 --- a/C7/Lua/texture_configs/civ3/terrain.lua +++ b/C7/Lua/texture_configs/civ3/terrain.lua @@ -86,6 +86,8 @@ terrain.marsh = { terrain.river = TERRAIN .. "mtnRivers.pcx" +terrain.river_delta = TERRAIN .. "deltaRivers.pcx" + terrain.tnt = TERRAIN .. "tnt.pcx" terrain.jungle = { diff --git a/C7/MapView.cs b/C7/MapView.cs index 6eba19749..adbed82a0 100644 --- a/C7/MapView.cs +++ b/C7/MapView.cs @@ -362,43 +362,80 @@ public partial class RiverLayer : LooseLayer { public static readonly Vector2 riverSize = new Vector2(128, 64); public static readonly Vector2 riverCenterOffset = new Vector2(riverSize.X / 2, 0); private ImageTexture riverTexture; + private ImageTexture riverDeltaTexture; public RiverLayer() { + // Both textures are 4x4 grids of equal dimensions + // The delta texture has four proper deltas textures and 12 other river segments riverTexture = TextureLoader.Load("terrain.river"); + riverDeltaTexture = TextureLoader.Load("terrain.river_delta"); } public override void drawObject(LooseView looseView, GameData gameData, Tile tile, Vector2 tileCenter) { - //The "point" is the easternmost point of the tile for which we are drawing rivers. - //Which river graphics to used is calculated by evaluating the tiles that neighbor - //that point. + // The point where four terrain tiles meet and where we want to center our river texture. + // It is the easternmost point of the current tile. + Vector2 thePoint = tileCenter + riverCenterOffset; + + // We draw the texture centered on the point by starting the draw from half of its width away + Vector2 drawOffset = -0.5f * riverSize; + + // The right river texture is calculated by evaluating the tiles around the point Tile northOfPoint = tile.neighbors[TileDirection.NORTHEAST]; Tile eastOfPoint = tile.neighbors[TileDirection.EAST]; - Tile westOfPoint = tile; Tile southOfPoint = tile.neighbors[TileDirection.SOUTHEAST]; + Tile westOfPoint = tile; - int riverGraphicsIndex = 0; + var (row, col, idx) = DeriveTextureIndex(northOfPoint, eastOfPoint, southOfPoint, westOfPoint); + if (row < 0 || col < 0) + return; - if (northOfPoint.riverSouthwest) { - riverGraphicsIndex++; - } - if (eastOfPoint.riverNorthwest) { - riverGraphicsIndex += 2; + Rect2 riverRectangle = new Rect2(col * riverSize.X, row * riverSize.Y, riverSize); + Rect2 screenTarget = new Rect2(thePoint + drawOffset, riverSize); + + // Draw a special river delta texture in certain situations, otherwise draw from the regular river texture. + // Note that delta detection can be a forgiving heuristic, as the delta texture also features river elements. + if (IsDelta(idx, northOfPoint, eastOfPoint, southOfPoint, westOfPoint)) + looseView.DrawTextureRectRegion(riverDeltaTexture, screenTarget, riverRectangle); + else + looseView.DrawTextureRectRegion(riverTexture, screenTarget, riverRectangle); + } + + private static (int, int, int) DeriveTextureIndex(Tile north, Tile east, Tile south, Tile west) { + var textureIndex = 0; + + if (north.riverSouthwest) { + textureIndex++; } - if (westOfPoint.riverSoutheast) { - riverGraphicsIndex += 4; + if (east.riverNorthwest) { + textureIndex += 2; } - if (southOfPoint.riverNortheast) { - riverGraphicsIndex += 8; + if (west.riverSoutheast) { + textureIndex += 4; } - if (riverGraphicsIndex == 0) { - return; + if (south.riverNortheast) { + textureIndex += 8; } - int riverRow = riverGraphicsIndex / 4; - int riverColumn = riverGraphicsIndex % 4; - Rect2 riverRectangle = new Rect2(riverColumn * riverSize.X, riverRow * riverSize.Y, riverSize); - Rect2 screenTarget = new Rect2(tileCenter - (float)0.5 * riverSize + riverCenterOffset, riverSize); - looseView.DrawTextureRectRegion(riverTexture, screenTarget, riverRectangle); + if (textureIndex == 0) + return (-1, -1, -1); + + // We will index into a 4x4 texture matrix + return (textureIndex / 4, textureIndex % 4, textureIndex); + } + + private bool IsDelta(int textureIndex, Tile north, Tile east, Tile south, Tile west) { + // The placement calculation is the same for both river tiles and delta tiles. + // We simply draw from a different texture if we are on the coast. + + // The delta texture is set up such that the deltas only appear specific indexes. + if (textureIndex is not (1 or 2 or 4 or 8)) + return false; + + // We could get fancy with our delta heuristic, but this simple thing works quite well. + if (north.IsWater() || east.IsWater() || south.IsWater() || west.IsWater()) + return true; + + return false; } } From 89e1ca0a245b0e7f92ed2b1285a5af4e915a9470 Mon Sep 17 00:00:00 2001 From: Antti Halme Date: Sat, 21 Feb 2026 23:15:40 +0000 Subject: [PATCH 2/3] Make RiverLayer more precise and default to the narrow river tiles --- C7/MapView.cs | 64 ++++++++++++++++++++++++++++----------------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/C7/MapView.cs b/C7/MapView.cs index adbed82a0..c9dc57c51 100644 --- a/C7/MapView.cs +++ b/C7/MapView.cs @@ -361,14 +361,15 @@ public override void drawObject(LooseView looseView, GameData gameData, Tile til public partial class RiverLayer : LooseLayer { public static readonly Vector2 riverSize = new Vector2(128, 64); public static readonly Vector2 riverCenterOffset = new Vector2(riverSize.X / 2, 0); - private ImageTexture riverTexture; - private ImageTexture riverDeltaTexture; + private ImageTexture narrowTexture; + private ImageTexture broadTexture; public RiverLayer() { // Both textures are 4x4 grids of equal dimensions - // The delta texture has four proper deltas textures and 12 other river segments - riverTexture = TextureLoader.Load("terrain.river"); - riverDeltaTexture = TextureLoader.Load("terrain.river_delta"); + // The narrow texture has four river deltas and 12 narrow river segments + // The broad texture has 16 broad river segments with more meandering + narrowTexture = TextureLoader.Load("terrain.river_delta"); + broadTexture = TextureLoader.Load("terrain.river"); } public override void drawObject(LooseView looseView, GameData gameData, Tile tile, Vector2 tileCenter) { @@ -386,36 +387,52 @@ public override void drawObject(LooseView looseView, GameData gameData, Tile til Tile westOfPoint = tile; var (row, col, idx) = DeriveTextureIndex(northOfPoint, eastOfPoint, southOfPoint, westOfPoint); - if (row < 0 || col < 0) + if (row < 0 || col < 0 || idx < 0) return; Rect2 riverRectangle = new Rect2(col * riverSize.X, row * riverSize.Y, riverSize); Rect2 screenTarget = new Rect2(thePoint + drawOffset, riverSize); - // Draw a special river delta texture in certain situations, otherwise draw from the regular river texture. - // Note that delta detection can be a forgiving heuristic, as the delta texture also features river elements. - if (IsDelta(idx, northOfPoint, eastOfPoint, southOfPoint, westOfPoint)) - looseView.DrawTextureRectRegion(riverDeltaTexture, screenTarget, riverRectangle); + // Draw a river texture from one of the textures, depending on terrain. + // The placement calculation is the same for both river textures. + + if (idx is 1 or 2 or 4 or 8) + { + // Narrow set has deltas at these indexes + if (HasWater(northOfPoint, eastOfPoint, southOfPoint, westOfPoint)) + looseView.DrawTextureRectRegion(narrowTexture, screenTarget, riverRectangle); + else + looseView.DrawTextureRectRegion(broadTexture, screenTarget, riverRectangle); + } else - looseView.DrawTextureRectRegion(riverTexture, screenTarget, riverRectangle); + { + // TODO: When to draw from the broad texture? Is it semi-random? Is it based on elevation? + + // Default: narrow rivers, less meandering + looseView.DrawTextureRectRegion(narrowTexture, screenTarget, riverRectangle); + } } private static (int, int, int) DeriveTextureIndex(Tile north, Tile east, Tile south, Tile west) { var textureIndex = 0; - if (north.riverSouthwest) { - textureIndex++; + if (north.riverSouthwest || west.riverNortheast) { + textureIndex += 1; } - if (east.riverNorthwest) { + if (east.riverNorthwest || north.riverSoutheast) { textureIndex += 2; } - if (west.riverSoutheast) { + if (west.riverSoutheast || south.riverNorthwest) { textureIndex += 4; } - if (south.riverNortheast) { + if (south.riverNortheast || east.riverSouthwest) { textureIndex += 8; } + // Passes "The Point" only: an oasis + if (textureIndex == 0 && (north.riverSouth || east.riverWest || west.riverEast || south.riverNorth)) + return (0, 0, 0); + if (textureIndex == 0) return (-1, -1, -1); @@ -423,19 +440,8 @@ private static (int, int, int) DeriveTextureIndex(Tile north, Tile east, Tile so return (textureIndex / 4, textureIndex % 4, textureIndex); } - private bool IsDelta(int textureIndex, Tile north, Tile east, Tile south, Tile west) { - // The placement calculation is the same for both river tiles and delta tiles. - // We simply draw from a different texture if we are on the coast. - - // The delta texture is set up such that the deltas only appear specific indexes. - if (textureIndex is not (1 or 2 or 4 or 8)) - return false; - - // We could get fancy with our delta heuristic, but this simple thing works quite well. - if (north.IsWater() || east.IsWater() || south.IsWater() || west.IsWater()) - return true; - - return false; + private bool HasWater(Tile north, Tile east, Tile south, Tile west) { + return north.IsWater() || east.IsWater() || south.IsWater() || west.IsWater(); } } From 605dd10599300fe793ea711677d5de315f3bc015 Mon Sep 17 00:00:00 2001 From: Antti Halme Date: Sat, 21 Feb 2026 23:33:54 +0000 Subject: [PATCH 3/3] fmt --- C7/MapView.cs | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/C7/MapView.cs b/C7/MapView.cs index c9dc57c51..f048690f6 100644 --- a/C7/MapView.cs +++ b/C7/MapView.cs @@ -396,20 +396,17 @@ public override void drawObject(LooseView looseView, GameData gameData, Tile til // Draw a river texture from one of the textures, depending on terrain. // The placement calculation is the same for both river textures. - if (idx is 1 or 2 or 4 or 8) - { + if (idx is 1 or 2 or 4 or 8) { // Narrow set has deltas at these indexes if (HasWater(northOfPoint, eastOfPoint, southOfPoint, westOfPoint)) looseView.DrawTextureRectRegion(narrowTexture, screenTarget, riverRectangle); else looseView.DrawTextureRectRegion(broadTexture, screenTarget, riverRectangle); - } - else - { + } else { // TODO: When to draw from the broad texture? Is it semi-random? Is it based on elevation? // Default: narrow rivers, less meandering - looseView.DrawTextureRectRegion(narrowTexture, screenTarget, riverRectangle); + looseView.DrawTextureRectRegion(narrowTexture, screenTarget, riverRectangle); } }