• We are currently performing site maintenance, parts of civfanatics are currently offline, but will come back online in the coming days. For more updates please see here.

Map script request: blend of Continents and Fractal

I'm still doing some tinkering.

Anyone know what these parameters do?
Code:
    -- Generate Large Islands  
    local args = {};  
    islands = plotTypes;
    args.iWaterPercent = 75 + water_percent_modifier;
    args.iRegionWidth = math.ceil(g_iW);
    args.iRegionHeight = math.ceil(g_iH);
    args.iRegionWestX = math.floor(0);
    args.iRegionSouthY = math.floor(0);
    args.iRegionGrain = 3;
    args.iRegionHillsGrain = 4;
    args.iRegionPlotFlags = g_iFlags;
    args.iRegionFracXExp = 6;
    args.iRegionFracYExp = 5;
    plotTypes = GenerateFractalLayerWithoutHills(args, plotTypes);
In particular, these parameters:
Code:
    args.iRegionWidth = math.ceil(g_iW);
    args.iRegionHeight = math.ceil(g_iH);
    args.iRegionWestX = math.floor(0);
    args.iRegionSouthY = math.floor(0);

    args.iRegionFracXExp = 6;
    args.iRegionFracYExp = 5;
I want to see if I can make it less likely that islands will form in polar regions, and at map edges.

I wonder if something like this would work to create an area three hexes from the edge of the map in which extra islands will not generate.
Code:
    args.iRegionWidth = math.ceil(g_iW/3.0);
    args.iRegionHeight = math.ceil(g_iH/3.0);
    args.iRegionWestX = math.floor(3);
    args.iRegionSouthY = math.floor(3);
 
Anyone know what these parameters do?
Code:
    -- Generate Large Islands 
    local args = {}; 
    islands = plotTypes;
    args.iWaterPercent = 75 + water_percent_modifier;
    args.iRegionWidth = math.ceil(g_iW);
    args.iRegionHeight = math.ceil(g_iH);
    args.iRegionWestX = math.floor(0);
    args.iRegionSouthY = math.floor(0);
    args.iRegionGrain = 3;
    args.iRegionHillsGrain = 4;
    args.iRegionPlotFlags = g_iFlags;
    args.iRegionFracXExp = 6;
    args.iRegionFracYExp = 5;
    plotTypes = GenerateFractalLayerWithoutHills(args, plotTypes);
Those parameters feed into GenerateFractalLayerWithoutHills(), which generates a fractal land/water layer and adds that layer to the map. The Island Plates and Seven Seas map types use this function to create multi-layered maps:

iWaterPercent = how much of the layer will be water versus land

Region parameters:
  • Width, Height, WestX, SouthY: the bounding box of the new layer
  • Grain (integer): granlarity of the fractal where 1 = pangea or inland sea and 5 = tiny islands or tiny lakes
  • HillsGrain (integer): unused, I think
  • PlotFlags: fractal behavior flags such as FRAC_POLAR, FRAC_WRAP_X, and FRAC_WRAP_Y
  • FracXExp, FracYExp: fractal "exponents"...not exactly sure what these do; I recommend leaving these alone or maybe setting them to -1.
In particular, these parameters:
Code:
    args.iRegionWidth = math.ceil(g_iW);
    args.iRegionHeight = math.ceil(g_iH);
    args.iRegionWestX = math.floor(0);
    args.iRegionSouthY = math.floor(0);

    args.iRegionFracXExp = 6;
    args.iRegionFracYExp = 5;
This is the bounding box of the new fractal layer to add to the map. See above.


I want to see if I can make it less likely that islands will form in polar regions, and at map edges.

I wonder if something like this would work to create an area three hexes from the edge of the map in which extra islands will not generate.
Code:
    args.iRegionWidth = math.ceil(g_iW/3.0);
    args.iRegionHeight = math.ceil(g_iH/3.0);
    args.iRegionWestX = math.floor(3);
    args.iRegionSouthY = math.floor(3);
That would create a bounding box that's a third the width and height of the map whose SW corner is at (3,3).

Sounds like you want to do this instead:
Code:
    args.iRegionWidth = g_iW - 6;
    args.iRegionHeight = g_iH - 6;
    args.iRegionWestX = 3;
    args.iRegionSouthY = 3;

To avoid map edges, you can also set this:
Code:
   args.iRegionPlotFlags = {FRAC_POLAR = true};
This will make the fractal use polar coordinates, which generally avoids map edges.

Of course, you can always do brute force by just looping through the plots and manually setting plots close to the edges to g_PLOT_TYPE_OCEAN.
 
Those parameters feed into GenerateFractalLayerWithoutHills(), which generates a fractal land/water layer and adds that layer to the map. The Island Plates and Seven Seas map types use this function to create multi-layered maps:

iWaterPercent = how much of the layer will be water versus land

Region parameters:
  • Width, Height, WestX, SouthY: the bounding box of the new layer
  • Grain (integer): granlarity of the fractal where 1 = pangea or inland sea and 5 = tiny islands or tiny lakes
  • HillsGrain (integer): unused, I think
  • PlotFlags: fractal behavior flags such as FRAC_POLAR, FRAC_WRAP_X, and FRAC_WRAP_Y
  • FracXExp, FracYExp: fractal "exponents"...not exactly sure what these do; I recommend leaving these alone or maybe setting them to -1.

This is the bounding box of the new fractal layer to add to the map. See above.



That would create a bounding box that's a third the width and height of the map whose SW corner is at (3,3).

Sounds like you want to do this instead:
Code:
    args.iRegionWidth = g_iW - 6;
    args.iRegionHeight = g_iH - 6;
    args.iRegionWestX = 3;
    args.iRegionSouthY = 3;

To avoid map edges, you can also set this:
Code:
   args.iRegionPlotFlags = {FRAC_POLAR = true};
This will make the fractal use polar coordinates, which generally avoids map edges.

Of course, you can always do brute force by just looping through the plots and manually setting plots close to the edges to g_PLOT_TYPE_OCEAN.
Thanks.

I'll experiment with your suggested code.

For the code:
Code:
   args.iRegionPlotFlags = {FRAC_POLAR = true};
Would that replace the existing args.iRegionPlotFlags code line, or be added as a separate line?
 
The changing the X-Y layer co-ordinates didn't really work. It was producing horizontal streaks, and the vertical line cut-off looked unnatural going through islands.

I notice in other maps that there are often horizontal lines near the poles. I wonder if there's something going on where this is meant to imitate the curvature of the planet. So maybe raising the X co-ordinate raises these horizontal streaks to appear further into the map.

The args.iRegionPlotFlags = {FRAC_POLAR = true}; code is more interesting. It always generates a Pacific-style empty ocean at the map edge, so there isn't overlapping land going over the map edges, although the empty ocean does also mean some islands are lost.

Overall, though, I think it's a good map script. It generates varied maps from Pangaea-style to separate continents and islands. I think it pretty much does feel like a blend of the vanilla Continents and Fractal map scripts.

9X0vFtm.png
 
The args.iRegionPlotFlags = {FRAC_POLAR = true}; code is more interesting. It always generates a Pacific-style empty ocean at the map edge, so there isn't overlapping land going over the map edges, although the empty ocean does also mean some islands are lost.
To reduce the chance of having a Pacific Ocean, try this instead:
Code:
args.iRegionPlotFlags = {FRAC_WRAP_X = true, FRAC_POLAR = true};
This allows the fractal to touch the east-west edges of the map.
 
I've discovered a problem, although I don't know whether it's specific to this map script.

Occasionally, rivers generate that empty into lakes. These rivers do not have names, and do not have the "River(s): River [X]" line in the tooltip infobox that appears when mouse-overing tiles adjacent to rivers.

Yet they have floodplains, so that could possibly - I don't know for sure - lead to flooding disasters, but no way to mitigate them through building a Dam.

Here are a couple of examples:

Spoiler :

DwinKKv.png


Sk1lNDz.png

 
I expect this may be an issue specific to the map script.

Here are mentions of "river" in the map script:
Code:
include "RiversLakes"

...

    -- River generation is affected by plot types, originating from highlands and preferring to traverse lowlands.
    AddRivers();
   
    -- Lakes would interfere with rivers, causing them to stop and not reach the ocean, if placed any sooner.
    local numLargeLakes = GameInfo.Maps[Map.GetMapSize()].Continents
    AddLakes(numLargeLakes);
   
...

    featuregen:AddFeatures(true, true);  --second parameter is whether or not rivers start inland);
If I was to hazard a guess as to what the problem is, then it would be that the script has added code telling it to add large islands. Since I have decreased map Grain, it is possible for the islands to join with the continents created by the main code. So these may turn rivers that were going into sea into rivers going into lakes.

So the solution, if there is one, I presume would be to move the code telling it to generate large islands, and put it before the code that generates the rivers and lakes. Hopefully, that is possible and won't lead to further issues.
 
If I was to hazard a guess as to what the problem is, then it would be that the script has added code telling it to add large islands.
Unless you added that code yourself, the script should have already added islands by that point.

The cause of this is probably in the river generator, which lives in <Civ VI install dir>\DLC\Expansion2\Maps\Utility\RiversLakes.lua. The river generator assumes that the map has no lakes, however your map script might be generating lakes due to snaky fractal patterns and/or extra islands merging with the 1st layer of land (as you correctly suspected). My guess is that the river generator simply mistook lake tiles for coast tiles.

That said, I'm not sure if I've ever seen a river erroneously not appear in a tooltip before, and I've played games that had rivers flowing into lakes. That tooltip in your screenshot looks like that of a mountain that is at the source of a river, but not next to a river.

To avoid this, you might have to go a step farther and actually remove lakes before generating rivers.
 
Unless you added that code yourself, the script should have already added islands by that point.
Here's the code - it's basically the 'Splintered Fractal' map with adjusted Continent Grain and Island Grain, adjusted sea level, and the args.iRegionPlotFlags = {FRAC_POLAR = true}; line in the 'Generate Large Islands' section of code. The ' -- Generate Large Islands' section is separate from the other land generation code, but since large islands can have rivers, I guess rivers are being applied after.
Spoiler :

Code:
------------------------------------------------------------------------------
--	FILE:	 Splintered_Fractal.lua
--	AUTHOR:  
--	PURPOSE: Base game script - Produces widely varied continents.
------------------------------------------------------------------------------
--	Copyright (c) 2014 Firaxis Games, Inc. All rights reserved.
------------------------------------------------------------------------------

include "MapEnums"
include "MapUtilities"
include "MountainsCliffs"
include "RiversLakes"
include "FeatureGenerator"
include "TerrainGenerator"
include "NaturalWonderGenerator"
include "ResourceGenerator"
include "CoastalLowlands"
include "AssignStartingPlots"

local g_iW, g_iH;
local g_iFlags = {};
local g_continentsFrac = nil;
local featureGen = nil;
local world_age_new = 5;
local world_age_normal = 3;
local world_age_old = 2;
local islands = {};

-------------------------------------------------------------------------------
function GenerateMap()
	print("Generating Splintered Fractal Map");
	local pPlot;

	-- Set globals
	g_iW, g_iH = Map.GetGridSize();
	g_iFlags = TerrainBuilder.GetFractalFlags();

	local temperature = MapConfiguration.GetValue("temperature"); -- Default setting is Temperate.
	if temperature == 4 then
		temperature  =  1 + TerrainBuilder.GetRandomNumber(3, "Random Temperature- Lua");
	end
	
	--	local world_age
	local world_age = MapConfiguration.GetValue("world_age");
	if (world_age == 1) then
		world_age = world_age_new;
	elseif (world_age == 2) then
		world_age = world_age_normal;
	elseif (world_age == 3) then
		world_age = world_age_old;
	else
		world_age = 2 + TerrainBuilder.GetRandomNumber(4, "Random World Age - Lua");
	end

	plotTypes = GeneratePlotTypes(world_age);
	terrainTypes = GenerateTerrainTypes(plotTypes, g_iW, g_iH, g_iFlags, false, temperature);
	ApplyBaseTerrain(plotTypes, terrainTypes, g_iW, g_iH);
	
	AreaBuilder.Recalculate();
	TerrainBuilder.AnalyzeChokepoints();
	TerrainBuilder.StampContinents();

	local iContinentBoundaryPlots = GetContinentBoundaryPlotCount(g_iW, g_iH);
	local biggest_area = Areas.FindBiggestArea(false);
	print("After Adding Hills: ", biggest_area:GetPlotCount());
	AddTerrainFromContinents(plotTypes, terrainTypes, world_age, g_iW, g_iH, iContinentBoundaryPlots);

	AreaBuilder.Recalculate();

	-- River generation is affected by plot types, originating from highlands and preferring to traverse lowlands.
	AddRivers();
	
	-- Lakes would interfere with rivers, causing them to stop and not reach the ocean, if placed any sooner.
	local numLargeLakes = GameInfo.Maps[Map.GetMapSize()].Continents
	AddLakes(numLargeLakes);

	AddFeatures();
	TerrainBuilder.AnalyzeChokepoints();
	
	print("Adding cliffs");
	AddCliffs(plotTypes, terrainTypes);

	local args = {
		numberToPlace = GameInfo.Maps[Map.GetMapSize()].NumNaturalWonders,
	};
	local nwGen = NaturalWonderGenerator.Create(args);
	
	AddFeaturesFromContinents();
	MarkCoastalLowlands();
	
	--for i = 0, (g_iW * g_iH) - 1, 1 do
		--pPlot = Map.GetPlotByIndex(i);
		--print ("i: plotType, terrainType, featureType: " .. tostring(i) .. ": " .. tostring(plotTypes[i]) .. ", " .. tostring(terrainTypes[i]) .. ", " .. tostring(pPlot:GetFeatureType(i)));
	--end
	local resourcesConfig = MapConfiguration.GetValue("resources");
	local startconfig = MapConfiguration.GetValue("start"); -- Get the start config
	local args = {
		resources = resourcesConfig,
		START_CONFIG = startConfig,
	};
	local resGen = ResourceGenerator.Create(args);

	print("Creating start plot database.");
	
	-- START_MIN_Y and START_MAX_Y is the percent of the map ignored for major civs' starting positions.
	local args = {
		MIN_MAJOR_CIV_FERTILITY = 175,
		MIN_MINOR_CIV_FERTILITY = 50, 
		MIN_BARBARIAN_FERTILITY = 1,
		START_MIN_Y = 15,
		START_MAX_Y = 15,
		START_CONFIG = startconfig,
	};
	local start_plot_database = AssignStartingPlots.Create(args)

	local GoodyGen = AddGoodies(g_iW, g_iH);
end


-------------------------------------------------------------------------------
function GeneratePlotTypes(world_age)
	print("Generating Plot Types");
	local plotTypes = {};
	
	local sea_level_low = 67;
	local sea_level_normal = 74;
	local sea_level_high = 80;
	local extra_mountains = 0;
	local adjust_plates = 1.0;
	local shift_plot_types = true;
	local hills_ridge_flags = g_iFlags;
	local peaks_ridge_flags = g_iFlags;
	local has_center_rift = false;

	--	local sea_level
    local sea_level = MapConfiguration.GetValue("sea_level");
	local water_percent;
	local water_percent_modifier = 0;
	if sea_level == 1 then -- Low Sea Level
		water_percent = sea_level_low
		water_percent_modifier = -4
	elseif sea_level == 2 then -- Normal Sea Level
		water_percent =sea_level_normal
		water_percent_modifier = 4;
	elseif sea_level == 3 then -- High Sea Level
		water_percent = sea_level_high
	else
		water_percent = TerrainBuilder.GetRandomNumber(sea_level_high- sea_level_low, "Random Sea Level - Lua") + sea_level_low  + 1 ;
		water_percent_modifier = TerrainBuilder.GetRandomNumber(9, "Random Sea Level - Lua") - 4;
	end

	-- Set values for hills and mountains according to World Age chosen by user.
	local adjustment = world_age;
	if world_age <= world_age_old  then -- 5 Billion Years
		adjust_plates = adjust_plates * 0.75;
	elseif world_age >= world_age_new then -- 3 Billion Years
		adjust_plates = adjust_plates * 1.5;
	else -- 4 Billion Years
	end

	local hillsBottom1 = 28 - adjustment;
	local hillsTop1 = 28 + adjustment;
	local hillsBottom2 = 72 - adjustment;
	local hillsTop2 = 72 + adjustment;
	local hillsClumps = 1 + adjustment;
	local hillsNearMountains = 91 - (adjustment * 2) - extra_mountains;
	local mountains = 97 - adjustment - extra_mountains;

	local polar =  true;

	local fracFlags = {};

	fracFlags.FRAC_POLAR = true;
	local MapSizeTypes = {};
	for row in GameInfo.Maps() do
		MapSizeTypes[row.MapSizeType] = row.PlateValue;
	end
	local sizekey = Map.GetMapSize();
	local numPlates = MapSizeTypes[sizekey] or 4;

	local continent_grain = 2;
	local rift_grain = -1;

	local riftsFrac = Fractal.Create(g_iW, g_iH, rift_grain, {}, 6, 5);
	g_continentsFrac = Fractal.CreateRifts(g_iW, g_iH, continent_grain, fracFlags, riftsFrac, 6, 5);
	g_continentsFrac:BuildRidges(numPlates, {}, 1, 2);
	
	hillsFrac = Fractal.Create(g_iW, g_iH, continent_grain, {}, 6, 5);
	mountainsFrac = Fractal.Create(g_iW, g_iH, continent_grain, {}, 6, 5);
	hillsFrac:BuildRidges(numPlates, g_iFlags, 1, 2);
	mountainsFrac:BuildRidges(numPlates * 2/3, g_iFlags, 6, 1);
	local iWaterThreshold = g_continentsFrac:GetHeight(water_percent);	
	local iHillsBottom1 = hillsFrac:GetHeight(hillsBottom1);
	local iHillsTop1 = hillsFrac:GetHeight(hillsTop1);
	local iHillsBottom2 = hillsFrac:GetHeight(hillsBottom2);
	local iHillsTop2 = hillsFrac:GetHeight(hillsTop2);
	local iHillsClumps = mountainsFrac:GetHeight(hillsClumps);
	local iHillsNearMountains = mountainsFrac:GetHeight(hillsNearMountains);
	local iMountainThreshold = mountainsFrac:GetHeight(mountains);
	local iPassThreshold = hillsFrac:GetHeight(hillsNearMountains);
	local iMountain100 = mountainsFrac:GetHeight(100);
	local iMountain99 = mountainsFrac:GetHeight(99);
	local iMountain97 = mountainsFrac:GetHeight(97);
	local iMountain95 = mountainsFrac:GetHeight(95);

	for x = 0, g_iW - 1 do
		for y = 0, g_iH - 1 do
			local i = y * g_iW + x + 1;
			local val = g_continentsFrac:GetHeight(x, y);
			local mountainVal = mountainsFrac:GetHeight(x, y);
			local hillVal = hillsFrac:GetHeight(x, y);
			local pPlot = Map.GetPlotByIndex(i);
	
			if(val <= iWaterThreshold) then
				plotTypes[i] = g_PLOT_TYPE_OCEAN;
				TerrainBuilder.SetTerrainType(pPlot, g_TERRAIN_TYPE_OCEAN);  -- temporary setting so can calculate areas

				if (mountainVal == iMountain100) then -- Isolated peak in the ocean
					plotTypes[i] = g_PLOT_TYPE_MOUNTAIN;
					TerrainBuilder.SetTerrainType(pPlot, g_TERRAIN_TYPE_DESERT);  -- temporary setting so can calculate areas
				elseif (mountainVal == iMountain99) then
					plotTypes[i] = g_PLOT_TYPE_HILLS;
					TerrainBuilder.SetTerrainType(pPlot, g_TERRAIN_TYPE_DESERT);  -- temporary setting so can calculate areas
				elseif (mountainVal == iMountain97) or (mountainVal == iMountain95) then
					plotTypes[i] = g_PLOT_TYPE_LAND;
					TerrainBuilder.SetTerrainType(pPlot, g_TERRAIN_TYPE_DESERT);  -- temporary setting so can calculate areas
				end
			else
				if (mountainVal >= iMountainThreshold) then
					if (hillVal >= iPassThreshold) then -- Mountain Pass though the ridgeline - Brian
						plotTypes[i] = g_PLOT_TYPE_HILLS;
						TerrainBuilder.SetTerrainType(pPlot, g_TERRAIN_TYPE_DESERT);  -- temporary setting so can calculate areas
					else -- Mountain
						plotTypes[i] = g_PLOT_TYPE_MOUNTAIN;
						TerrainBuilder.SetTerrainType(pPlot, g_TERRAIN_TYPE_DESERT);  -- temporary setting so can calculate areas
					end
				elseif (mountainVal >= iHillsNearMountains) then
					plotTypes[i] = g_PLOT_TYPE_HILLS;
					TerrainBuilder.SetTerrainType(pPlot, g_TERRAIN_TYPE_DESERT);  -- temporary setting so can calculate areas
				else
					if ((hillVal >= iHillsBottom1 and hillVal <= iHillsTop1) or (hillVal >= iHillsBottom2 and hillVal <= iHillsTop2)) then
						plotTypes[i] = g_PLOT_TYPE_HILLS;
						TerrainBuilder.SetTerrainType(pPlot, g_TERRAIN_TYPE_DESERT);  -- temporary setting so can calculate areas
					else
						plotTypes[i] = g_PLOT_TYPE_LAND;
						TerrainBuilder.SetTerrainType(pPlot, g_TERRAIN_TYPE_DESERT);  -- temporary setting so can calculate areas
					end
				end
			end
		end
	end
	
	ShiftPlotTypes(plotTypes);
	AreaBuilder.Recalculate();

	-- Generate Large Islands	
	local args = {};	
	islands = plotTypes;
	args.iWaterPercent = 75 + water_percent_modifier;
	args.iRegionWidth = math.ceil(g_iW);
	args.iRegionHeight = math.ceil(g_iH);
	args.iRegionWestX = math.floor(0);
	args.iRegionSouthY = math.floor(0);
	args.iRegionGrain = 2;
	args.iRegionHillsGrain = 4;
	args.iRegionPlotFlags = g_iFlags;
	args.iRegionPlotFlags = {FRAC_POLAR = true};
	args.iRegionFracXExp = 6;
	args.iRegionFracYExp = 5;
	plotTypes = GenerateFractalLayerWithoutHills(args, plotTypes);

	ShiftPlotTypes(plotTypes);
	AreaBuilder.Recalculate();

	local args = {};
	world_age = world_age;
	args.world_age = world_age;
	args.iW = g_iW;
	args.iH = g_iH
	args.iFlags = g_iFlags;
	args.tectonic_islands = false;
	args.blendRidge = 10;
	args.blendFract = 1;
	args.extra_mountains = 10;
	mountainRatio = 11 + world_age * 2;
	plotTypes = ApplyTectonics(args, plotTypes);
	plotTypes = AddLonelyMountains(plotTypes, mountainRatio);

	return plotTypes;
end

----------------------------------------------------------------------------------
function AddFeatures()
	print("Adding Features");

	-- Get Rainfall setting input by user.
	local rainfall = MapConfiguration.GetValue("rainfall");
	if rainfall == 4 then
		rainfall = 1 + TerrainBuilder.GetRandomNumber(3, "Random Rainfall - Lua");
	end
	
	local args = {rainfall = rainfall}
	featuregen = FeatureGenerator.Create(args);
	featuregen:AddFeatures(true, true);  --second parameter is whether or not rivers start inland);
end

function AddFeaturesFromContinents()
	print("Adding Features from Continents");

	featuregen:AddFeaturesFromContinents();
end

-------------------------------------------------------------------------------
function GenerateFractalLayerWithoutHills (args, plotTypes)
	local args = args or {};
	local plotTypes2 = {};

	-- Handle args or assign defaults.
	local iWaterPercent = args.iWaterPercent or 55;
	local iRegionWidth = args.iRegionWidth; -- Mandatory Parameter, no default
	local iRegionHeight = args.iRegionHeight; -- Mandatory Parameter, no default
	local iRegionWestX = args.iRegionWestX; -- Mandatory Parameter, no default
	local iRegionSouthY = args.iRegionSouthY; -- Mandatory Parameter, no default
	local iRegionGrain = args.iRegionGrain or 1;
	local iRegionPlotFlags = args.iRegionPlotFlags or g_iFlags;
	local iRegionTerrainFlags = g_iFlags; -- Removed from args list.
	local iRegionFracXExp = args.iRegionFracXExp or 6;
	local iRegionFracYExp = args.iRegionFracYExp or 5;
	local iRiftGrain = args.iRiftGrain or -1;
	local bShift = args.bShift or true;
	
	--print("Received Region Data");
	--print(iRegionWidth, iRegionHeight, iRegionWestX, iRegionSouthY, iRegionGrain);
	--print("- - -");
	
	--print("Filled regional table.");
	-- Loop through the region's plots
	for x = 0, iRegionWidth - 1, 1 do
		for y = 0, iRegionHeight - 1, 1 do
			local i = y * iRegionWidth + x + 1; -- Lua arrays start at 1.
			plotTypes2[i] =g_PLOT_TYPE_OCEAN;
		end
	end

	-- Init the land/water fractal
	local regionContinentsFrac;
	if(iRiftGrain > 0 and iRiftGrain < 4) then
		local riftsFrac = Fractal.Create(g_iW, g_iH, rift_grain, {}, iRegionFracXExp, iRegionFracYExp);
		regionContinentsFrac = Fractal.CreateRifts(g_iW, g_iH, iRegionGrain, iRegionPlotFlags, riftsFrac, iRegionFracXExp, iRegionFracYExp);
	else
		regionContinentsFrac = Fractal.Create(g_iW, g_iH, iRegionGrain, iRegionPlotFlags, iRegionFracXExp, iRegionFracYExp);	
	end
	--print("Initialized main fractal");
	local iWaterThreshold = regionContinentsFrac:GetHeight(iWaterPercent);

	-- Loop through the region's plots
	for x = 0, iRegionWidth - 1, 1 do
		for y = 0, iRegionHeight - 1, 1 do
			local i = y * iRegionWidth + x + 1; 
			local val = regionContinentsFrac:GetHeight(x,y);
			if val <= iWaterThreshold or Adjacent(i) == true then
				--do nothing
			else
				plotTypes2[i] = g_PLOT_TYPE_LAND;
			end
		end
	end

	if bShift then
		ShiftPlotTypes(plotTypes);
	end

	print("Shifted Plots - Width: ", iRegionWidth, "Height: ", iRegionHeight);

	-- Apply the region's plots to the global plot array.
	for x = 0, iRegionWidth - 1, 1 do
		local wholeworldX = x + iRegionWestX;
		for y = 0, iRegionHeight - 1, 1 do
			local index = y * iRegionWidth + x + 1
			if plotTypes2[index] ~= g_PLOT_TYPE_OCEAN then
				local wholeworldY = y + iRegionSouthY;
				local i = wholeworldY * g_iW + wholeworldX + 1
				plotTypes[i] = plotTypes2[index];
			end
		end
	end
	--print("Generated Plot Types");

	return plotTypes;
end

-------------------------------------------------------------------------------------------
function Adjacent(index)
	aIslands = islands;
	index = index -1;

	if(aIslands == nil) then
		return false;
	end
	
	if(index < 0) then
		return false
	end

	local plot = Map.GetPlotByIndex(index);
	if(aIslands[index] ~= nil and aIslands[index] == g_PLOT_TYPE_LAND) then
		return true;
	end

	for direction = 0, DirectionTypes.NUM_DIRECTION_TYPES - 1, 1 do
		local adjacentPlot = Map.GetAdjacentPlot(plot:GetX(), plot:GetY(), direction);
		if(adjacentPlot ~= nil) then
			local newIndex = adjacentPlot:GetIndex();
			if(aIslands  ~= nil and aIslands[newIndex] == g_PLOT_TYPE_LAND) then
				return true;
			end
		end
	end

	return false;
end


That said, I'm not sure if I've ever seen a river erroneously not appear in a tooltip before, and I've played games that had rivers flowing into lakes. That tooltip in your screenshot looks like that of a mountain that is at the source of a river, but not next to a river.
There's definitely no River info in the tooltip infobox.

See the following screenshot:
Spoiler :

PAULSrB.png


The cause of this is probably in the river generator, which lives in <Civ VI install dir>\DLC\Expansion2\Maps\Utility\RiversLakes.lua. The river generator assumes that the map has no lakes, however your map script might be generating lakes due to snaky fractal patterns and/or extra islands merging with the 1st layer of land (as you correctly suspected). My guess is that the river generator simply mistook lake tiles for coast tiles.

....

To avoid this, you might have to go a step farther and actually remove lakes before generating rivers.
That sounds like it might be a bit complicated, especially if it were to involve changes to the RiversLakes.Lua.

Also, removing lakes might also remove inland seas, which wouldn't be good. Are there any existing map scripts that remove lakes before generating rivers?

I fear it's probably back to the drawing board. Presumably, this issue would not arise if the added Generate Large Islands script weren't there, i.e. changing Grain alone is not enough to lead to this issue?
 
I just noticed that your first river-problem screenshot has an unnamed river flowing into the ocean on the west coast. There is also a named river flowing into the same lake that also has the mouth of an unnamed river. Not sure what to make of that...it's as if the sytem that assigns names to rivers just "gave up" at some point.

There's definitely no River info in the tooltip infobox.
Hmm, somehow the game can draw the river, but it can't tell that the river exists when checking adjacent plots. Out of curiosity, do you see the river in strategic view?

Stuff like this can be hard to diagnose and debug because there are multiple complex systems involved. FWIW, here is my guess at what happened:
  1. the river generator failed to finish making a river due to an "I think I'm done" bug (I'm not sure how this could have happened to you, but it wouldn't surprise me because the base game's river gen is convoluted)
  2. because the last segment of river was added without connecting to a body of water, the map generator failed to add the river to some sort of internal mapping of completed rivers (not sure if such an object even exists, but it looks like there is some magic that happens under the hood when adding rivers)
  3. the lake generator randomly added a lake that touched the failed river
  4. the game engine found a "river" next to a lake and decided to draw the river based on what's in the terrain map
  5. there is no name for the river because the river doesn't exist in the game's internal table of rivers
  6. the tooltip doesn't display the river because it doesn't have a name
  7. it's likely that this river also will never flood if it "doesn't exist" in the above-mentioned hypothetical mapping of rivers.
All I know for certain is that a river needs to have a mouth in order to exist--not sure what else could cause a river added to the map to not exist. That, and GS has special state for tracking things like volcano state (which volcanoes are active versus dormant), so it wouldn't surprise me if the same layer also tracked named rivers for flooding.

Also, removing lakes might also remove inland seas, which wouldn't be good. Are there any existing map scripts that remove lakes before generating rivers?
True, and that's probably why there aren't any existing map scripts that do that.

I fear it's probably back to the drawing board. Presumably, this issue would not arise if the added Generate Large Islands script weren't there, i.e. changing Grain alone is not enough to lead to this issue?
It's entirely possible that this bug exists even for base game map types (albeit at a lower chance of occurrence) and that you just never noticed it before. FWIW the InlandSea map type has its own river generator which might be less error-prone because it generates rivers mouth-to-source instead of source-to-mouth.
 
Hmm, somehow the game can draw the river, but it can't tell that the river exists when checking adjacent plots. Out of curiosity, do you see the river in strategic view?
I didn't save that map, so it's gone.

What code affects the number of landmasses. The Fractal map script tends towards a snakey Pangaea/single landmass, while the Small Continents map script always produces several separate continents/landmasses.
 
What code affects the number of landmasses. The Fractal map script tends towards a snakey Pangaea/single landmass, while the Small Continents map script always produces several separate continents/landmasses.
That would be the fractal grain, which controls the granularity of the land fractal for ocean-heavy map types. 1 = pangea, 2 = continents, 3 = small continents, 4 = islands, 5 = tiny islands.

Sea level can also affect the number of landmasses. At a low enough sea level, even tiny islands will merge into a snaky continents and then eventually a pangea-like blob. Setting this lower than 60% water makes the map start to look like Seven Seas.
 
Is fractal grain the same as continent_grain?

Small Continents map script has something different going on, as increasing grain still generates separate, but chunkier, continents.

The problem with increasing grain is that it frequently leads to maps with no small/medium islands. If one adds some additional code script to generate an island layer, then that runs the risk of the rivers issue again.

Do you know what this does?
Code:
    local adjust_plates = 1.0;
 
Yeah, continent_grain is the fractal grain for the fractal that makes land. You can actually use fractals for many things besides land, such as mountains.

Not exactly sure what adjust_plates does, but I think it's a way of subdividing the fractal that works well for generating mountains.

Edit: I just took a closer look at your code, and realized that adjust_plates is an unused variable, so my revised answer is that it does nothing.
 
Last edited:
Here's the result of playing around with the Pangaea map script. I removed the landmass percentage requirement and increased grain value (snakier), rather than decreasing it, as I had done with the other map script.

Here's another inland sea from that map script with a river that goes into it with floodplain that doesn't have a name.

Spoiler :
fFeVSYm.png


Also, here's a river from another map script (with decreased grain value) that has a river with no name going into Ocean.

Spoiler :
ozcax31.png
 
Last edited:
On a separate note, I had an idea. The Fractal map usually produces a Pangaea-like large land mass.

Would it be possible to take the Fractal map script, add some 'find biggest area' code to it, and use GetRandomNumber to randomly assign it different biggest landmass values.

e.g. Get random number up to 10

if roll 10 then check biggest area is 90%
if roll 9 then check biggest area is 80%
etc

Related code:
Code:
        AreaBuilder.Recalculate();
        local biggest_area = Areas.FindBiggestArea(false);
        iNumBiggestAreaTiles = biggest_area:GetPlotCount();
       
        -- Now test the biggest landmass to see if it is large enough.
        if iNumBiggestAreaTiles >= g_iNumTotalLandTiles * 0.84 then
            done = true;
            iBiggestID = biggest_area:GetID();
        end
        iAttempts = iAttempts + 1;
Code:
function GeneratePlotTypes()
    local mapType = TerrainBuilder.GetRandomNumber(10, "Random World Age - Lua");
    if(mapType < 5) then
        -- Fractal Map
        return FractalGenerator();
    elseif(mapType < 8) then
        -- Island Map
        return IslandPlates();
    else
        -- Continent Map
        return Continents ();
    end
end
 
Would it be possible to take the Fractal map script, add some 'find biggest area' code to it, and use GetRandomNumber to randomly assign it different biggest landmass values.

e.g. Get random number up to 10

if roll 10 then check biggest area is 90%
if roll 9 then check biggest area is 80%
You could do that, but the lower percentages might not be possible without increasing granularity (fractal grain). If you did RNG before the loop then you could potentially find yourself in an infinite loop of attempts. If you did RNG in the loop, then the lower percentages would probably always fail and you'd get some higher percentage instead.

Edited to clarify that granularity = fractal grain
 
Regarding the no-name rivers issue, it happens with or without the additional "Generate Large Islands" script, and with both increasing and decreasing grain. So why does this not happen when grain is at vanilla setting? I haven't tried it out to see whether it can happen on vanilla map scripts, but I think it would have been noticed by now. Aren't inland seas also possible with vanilla map scripts, such as Fractal?
 
Regarding the no-name rivers issue, it happens with or without the additional "Generate Large Islands" script, and with both increasing and decreasing grain. So why does this not happen when grain is at vanilla setting? I haven't tried it out to see whether it can happen on vanilla map scripts, but I think it would have been noticed by now. Aren't inland seas also possible with vanilla map scripts, such as Fractal?
Yeah, I'm starting to think that somehow your code/setup is corrupting the map. I looked at the Fractal script that you posted earlier, but didn't see any bugs that could screw-up the map enough to confuse the river system (such as resizing the map, setting plots to nil, etc). I'm assuming, however, that you didn't modify any of the base game's map utilities yourself, and that you're not running with any mods that mess with the map generator or its utilities.

Btw I also saw that you had mountains/hills logic and some leftover temporary terrain-setting stuff in your GeneratePlotTypes() function. You don't need that because the ApplyTectonics() function adds mountains and hills for you. Might as well remove it in case that was somehow the cause of the bug.

Yes, I think inland seas are possible with vanilla map scripts, such as Island Plates, Seven Seas, and Continents Islands. There's also Inland Sea, but you're probably thinking of which map types have multiple layers of land generation.
 
Last edited:
Back
Top Bottom