Graphics problems when placing additional natural wonders

I suppose send me a PM once you've released the next version and I'll at least mess around with it to see if I can reduce or eliminate the issues. I'm wondering if the GBR issues may be due to map.RecalculateAreas() and the many things tied into the divisions created and managed by that family of code. At the heart of the ghost tiles on map seams issues is an issue with either that function or the data it passes on to the graphics.dll. Certain tiles' heightmap information simply can't be rewritten once written to initially.

If all else fails, it may be possible to rewrite your entire mapscript to store all the data in a separate buffer until NWs have been determined. This would not be a simple undertaking but could be done. This should rule out issues with changing tiles which hopefully only leaves issues with placement rules as the possible cause of improperly displayed NWs.

The old placement rules can be interpreted from AssignStartingPlots.lua found in the vanilla Civ V stuff. However, between Vanilla and BNW they migrated all of these rules to XML. I'm not exactly sure which XML file they're in but that would be the place to look for the placement rules if you're messing with BNW.
 
Yeah that's what I did. I copied the placement data from BNW into my mod. If the user has the placement data I use theirs otherwise I use mine.

I figured that it's no big deal to copy this data. No extra features are enabled or anything like that. I wouldn't know how to do that even if I wanted to.
 
Here's some of the NW rules in AssignStartingPlots.lua, not just for the plot but also adjacent plots. I'm not sure if I got it all here:

Spoiler :
Code:
			local method_number = GameInfo.Natural_Wonder_Placement[row_number].TileChangesMethodNumber;
			if method_number ~= -1 then
				-- Custom method for tile changes needed by this wonder.
				NWCustomPlacement(x, y, row_number, method_number)
			else
				-- Check the XML data for any standard type tile changes, execute any that are indicated.
				if GameInfo.Natural_Wonder_Placement[row_number].ChangeCoreTileToMountain == true then
					if not plot:IsMountain() then
						plot:SetPlotType(PlotTypes.PLOT_MOUNTAIN, false, false);
					end
				elseif GameInfo.Natural_Wonder_Placement[row_number].ChangeCoreTileToFlatland == true then
					if plot:GetPlotType() ~= PlotTypes.PLOT_LAND then
						plot:SetPlotType(PlotTypes.PLOT_LAND, false, false);
					end
				end
				if GameInfo.Natural_Wonder_Placement[row_number].ChangeCoreTileTerrainToGrass == true then
					if plot:GetTerrainType() ~= TerrainTypes.TERRAIN_GRASS then
						plot:SetTerrainType(TerrainTypes.TERRAIN_GRASS, false, false);
					end
				elseif GameInfo.Natural_Wonder_Placement[row_number].ChangeCoreTileTerrainToPlains == true then
					if plot:GetTerrainType() ~= TerrainTypes.TERRAIN_PLAINS then
						plot:SetTerrainType(TerrainTypes.TERRAIN_PLAINS, false, false);
					end
				end
				if GameInfo.Natural_Wonder_Placement[row_number].SetAdjacentTilesToShallowWater == true then
					for loop, direction in ipairs(self.direction_types) do
						local adjPlot = Map.PlotDirection(x, y, direction)
						if adjPlot:GetTerrainType() ~= TerrainTypes.TERRAIN_COAST then
							adjPlot:SetTerrainType(TerrainTypes.TERRAIN_COAST, false, false)
						end
					end
				end
			end
And extra stuff for Reef and Rock of Gibraltar:
Spoiler :
Code:
------------------------------------------------------------------------------
--	FILE:	  NaturalWondersCustomMethods.lua
--	AUTHOR:   Bob Thomas
--	PURPOSE:  Functions designed to support custom natural wonder placement.
------------------------------------------------------------------------------
--	Copyright (c) 2011 Firaxis Games, Inc. All rights reserved.
------------------------------------------------------------------------------

--[[ -------------------------------------------------------------------------
NOTE: This file is an essential component of the Start Plot System. I have
separated out the functions in this file to permit more convenient operation
for modders wishing to add new natural wonders or modify existing ones. If
you are supplying new custom methods, you will not have to supply an updated
version of AssignStartingPlots with your mod; instead, you only have to supply
an update of this file along with your updated Civ5Features.xml file.

CONTENTS OF THIS FILE:

* NWCustomEligibility(x, y, method_number)
* NWCustomPlacement(x, y, row_number, method_number)
------------------------------------------------------------------------- ]]--

include("MapmakerUtilities");

------------------------------------------------------------------------------
function NWCustomEligibility(x, y, method_number)
	if method_number == 1 then
		-- This method checks a candidate plot for eligibility to be the Great Barrier Reef.
		local iW, iH = Map.GetGridSize();
		local plotIndex = y * iW + x + 1;
		-- We don't care about the center plot for this wonder. It can be forced. It's the surrounding plots that matter.
		-- This is also the only natural wonder type with a footprint larger than seven tiles.
		-- So first we'll check the extra tiles, make sure they are there, are ocean water, and have no Ice.
		local iNumCoast = 0;
		local extra_direction_types = {
			DirectionTypes.DIRECTION_EAST,
			DirectionTypes.DIRECTION_SOUTHEAST,
			DirectionTypes.DIRECTION_SOUTHWEST};
		local SEPlot = Map.PlotDirection(x, y, DirectionTypes.DIRECTION_SOUTHEAST)
		local southeastX = SEPlot:GetX();
		local southeastY = SEPlot:GetY();
		for loop, direction in ipairs(extra_direction_types) do -- The three plots extending another plot past the SE plot.
			local adjPlot = Map.PlotDirection(southeastX, southeastY, direction)
			if adjPlot == nil then
				return false
			end
			if adjPlot:IsWater() == false or adjPlot:IsLake() == true then
				return false
			end
			local featureType = adjPlot:GetFeatureType()
			if featureType ~= FeatureTypes.NO_FEATURE then
				return false
			end
			local terrainType = adjPlot:GetTerrainType()
			if terrainType == TerrainTypes.TERRAIN_COAST then
				iNumCoast = iNumCoast + 1;
			end
		end
		-- Now check the rest of the adjacent plots.
		local direction_types = { -- Not checking to southeast.
			DirectionTypes.DIRECTION_NORTHEAST,
			DirectionTypes.DIRECTION_EAST,
			DirectionTypes.DIRECTION_SOUTHWEST,
			DirectionTypes.DIRECTION_WEST,
			DirectionTypes.DIRECTION_NORTHWEST};
		for loop, direction in ipairs(direction_types) do
			local adjPlot = Map.PlotDirection(x, y, direction)
			if adjPlot:IsWater() == false then
				return false
			end
			local terrainType = adjPlot:GetTerrainType()
			if terrainType == TerrainTypes.TERRAIN_COAST then
				iNumCoast = iNumCoast + 1;
			end
		end
		-- If not enough coasts, reject this site.
		if iNumCoast < 4 then
			return false
		end
		-- This site is in the water, with at least some of the water plots being coast, so it's good.
		return true
	
	elseif method_number == 2 then
		-- This method checks a candidate plot for eligibility to be Rock of Gibraltar.
		local plot = Map.GetPlot(x, y);
		-- Checking center plot, which must be in the water or on the coast.
		local iW, iH = Map.GetGridSize();
		if plot:IsWater() == false and AdjacentToSaltWater(x, y) == false then
			return false
		end
		-- Now process the surrounding plots.
		local iNumLand, iNumCoast = 0, 0;
		local direction_types = {
			DirectionTypes.DIRECTION_NORTHEAST,
			DirectionTypes.DIRECTION_EAST,
			DirectionTypes.DIRECTION_SOUTHEAST,
			DirectionTypes.DIRECTION_SOUTHWEST,
			DirectionTypes.DIRECTION_WEST,
			DirectionTypes.DIRECTION_NORTHWEST
		};
		for loop, direction in ipairs(direction_types) do
			local adjPlot = Map.PlotDirection(x, y, direction)
			local plotType = adjPlot:GetPlotType();
			local terrainType = adjPlot:GetTerrainType()
			local featureType = adjPlot:GetFeatureType()
			if terrainType == TerrainTypes.TERRAIN_COAST and plot:IsLake() == false then
				if featureType == FeatureTypes.NO_FEATURE then
					iNumCoast = iNumCoast + 1;
				end
			end
			if plotType ~= PlotTypes.PLOT_OCEAN then
				iNumLand = iNumLand + 1;
			end
		end
		-- If too much land (or none), reject this site.
		if iNumLand ~= 1 then
			return false
		end
		-- If not enough coast, reject this site.
		if iNumCoast < 3 then
			return false
		end
		-- This site is good.
		return true

	-- These method numbers are not needed for the core game's natural wonders;
	-- however, this is where a modder could insert more custom methods, as needed.
	-- Any new methods added must be called from Natural_Wonder_Placement in Civ5Features.xml - Sirian, June 2011
	--
	--elseif method_number == 3 then
	--elseif method_number == 4 then
	--elseif method_number == 5 then

	else -- Unidentified Method Number
		return false
	end
end
------------------------------------------------------------------------------
function NWCustomPlacement(x, y, row_number, method_number)
	local iW, iH = Map.GetGridSize();
	if method_number == 1 then
		-- This method handles tile changes for the Great Barrier Reef.
		local plot = Map.GetPlot(x, y);
		if not plot:IsWater() then
			plot:SetPlotType(PlotTypes.PLOT_OCEAN, false, false);
		end
		if plot:GetTerrainType() ~= TerrainTypes.TERRAIN_COAST then
			plot:SetTerrainType(TerrainTypes.TERRAIN_COAST, false, false)
		end
		-- The Reef has a longer shape and demands unique handling. Process the extra plots.
		local extra_direction_types = {
			DirectionTypes.DIRECTION_EAST,
			DirectionTypes.DIRECTION_SOUTHEAST,
			DirectionTypes.DIRECTION_SOUTHWEST};
		local SEPlot = Map.PlotDirection(x, y, DirectionTypes.DIRECTION_SOUTHEAST)
		if not SEPlot:IsWater() then
			SEPlot:SetPlotType(PlotTypes.PLOT_OCEAN, false, false);
		end
		if SEPlot:GetTerrainType() ~= TerrainTypes.TERRAIN_COAST then
			SEPlot:SetTerrainType(TerrainTypes.TERRAIN_COAST, false, false)
		end
		if SEPlot:GetFeatureType() ~= FeatureTypes.NO_FEATURE then
			SEPlot:SetFeatureType(FeatureTypes.NO_FEATURE, -1)
		end
		local southeastX = SEPlot:GetX();
		local southeastY = SEPlot:GetY();
		for loop, direction in ipairs(extra_direction_types) do -- The three plots extending another plot past the SE plot.
			local adjPlot = Map.PlotDirection(southeastX, southeastY, direction)
			if adjPlot:GetTerrainType() ~= TerrainTypes.TERRAIN_COAST then
				adjPlot:SetTerrainType(TerrainTypes.TERRAIN_COAST, false, false)
			end
			local adjX = adjPlot:GetX();
			local adjY = adjPlot:GetY();
			local adjPlotIndex = adjY * iW + adjX + 1;
		end
		-- Now check the rest of the adjacent plots.
		local direction_types = { -- Not checking to southeast.
			DirectionTypes.DIRECTION_NORTHEAST,
			DirectionTypes.DIRECTION_EAST,
			DirectionTypes.DIRECTION_SOUTHWEST,
			DirectionTypes.DIRECTION_WEST,
			DirectionTypes.DIRECTION_NORTHWEST
			};
		for loop, direction in ipairs(direction_types) do
			local adjPlot = Map.PlotDirection(x, y, direction)
			if adjPlot:GetTerrainType() ~= TerrainTypes.TERRAIN_COAST then
				adjPlot:SetTerrainType(TerrainTypes.TERRAIN_COAST, false, false)
			end
		end
		-- Now place the Reef's second wonder plot. (The core method will place the main plot).
		local feature_type_to_place;
		for thisFeature in GameInfo.Features() do
			if thisFeature.Type == "FEATURE_REEF" then
				feature_type_to_place = thisFeature.ID;
				break
			end
		end
		SEPlot:SetFeatureType(feature_type_to_place);
	
	elseif method_number == 2 then
		-- This method handles tile changes for the Rock of Gibraltar.
		local plot = Map.GetPlot(x, y);
		plot:SetPlotType(PlotTypes.PLOT_LAND, false, false);
		plot:SetTerrainType(TerrainTypes.TERRAIN_GRASS, false, false)
		local direction_types = {
			DirectionTypes.DIRECTION_NORTHEAST,
			DirectionTypes.DIRECTION_EAST,
			DirectionTypes.DIRECTION_SOUTHEAST,
			DirectionTypes.DIRECTION_SOUTHWEST,
			DirectionTypes.DIRECTION_WEST,
			DirectionTypes.DIRECTION_NORTHWEST};
		for loop, direction in ipairs(direction_types) do
			local adjPlot = Map.PlotDirection(x, y, direction)
			if adjPlot:GetPlotType() == PlotTypes.PLOT_OCEAN then
				if adjPlot:GetTerrainType() ~= TerrainTypes.TERRAIN_COAST then
					adjPlot:SetTerrainType(TerrainTypes.TERRAIN_COAST, false, false)
				end
			else
				if adjPlot:GetPlotType() ~= PlotTypes.PLOT_MOUNTAIN then
					adjPlot:SetPlotType(PlotTypes.PLOT_MOUNTAIN, false, false);
				end
			end
		end

	-- These method numbers are not needed for the core game's natural wonders;
	-- however, this is where a modder could insert more custom methods, as needed.
	-- Any new methods added must be called from Natural_Wonder_Placement in Civ5Features.xml - Sirian, June 2011
	--
	--elseif method_number == 3 then
	--elseif method_number == 4 then
	--elseif method_number == 5 then

	end
end
------------------------------------------------------------------------------
Obviously there are still problems even after this code. But this is probably minimum to get these to work.
 
Oooh, I already see something in what Pazyryk pasted. The second "false" in the arguments for the SetTerrainType() method should be true. This argument is a flag specifically for RecalculateAreas(). Whenever you change a plot that's already been assigned to something else, it should be true. There's even some comments regarding this somewhere in the .lua's related to map generation (I quoted it some time ago in the PerfectWorld3 thread if you want to see for yourself).

Fixing this flag may or may not solve the issues with Gibraltar. Any ghost tiles I was getting in Perfect World 3 that weren't on the map seam were because this flag was set improperly in a couple of places throughout that script. Provided that testing this potential fix shows a marked improvement, fixing this unilaterally, for each version of the game, is more difficult due to the changes they've made to NW placement with the expansions. You may have to release separate versions of your mod, or come up with cases to detect which expansion(s) are currently being played (I used "if YieldTypes.YIELD_FAITH then" as a way to detect vanilla vs. any expansion as that detects whether or not Faith is defined).
 
You don't have to release separate versions of your mod. Here's what I did. The parts in bold are the important parts.

The passed table wonderSlot just contains some values used for placing one or more copies of a given natural wonder type.

The first bold segment within the function tries to use the player's placement values that are available to users that have a version higher than vanilla.

If no placement data is available then the second bold segment uses the placement data that I manually added in my mod using XML. It's just a copy of the latest placement data from BNW.

The third segment actually creates the wonder(s) (as long as any placement data was found for the current natural wonder).

Spoiler :
Code:
function PlacePlayerWonder(player, wonderSlot)
	local count = wonderSlot.count;
	local placement;
	
[B]	if GameInfo.Natural_Wonder_Placement ~= nil then
		for row in GameInfo.Natural_Wonder_Placement() do
			if row.NaturalWonderType == wonderSlot.featureType then
				placement = GameInfo.Natural_Wonder_Placement[row.ID];
				break;
			end
		end
	end

	if placement == nil then
		for row in GameInfo.GTAS_Natural_Wonder_Placement() do
			if row.NaturalWonderType == wonderSlot.featureType then
				placement = GameInfo.GTAS_Natural_Wonder_Placement[row.ID];
				break;
			end
		end
	end[/B]
	
	if placement ~= nil then
                -- This is where you would put your code for creating wonders using the placement data.

                -- Here is my code as an example.
		for plot in GetPlotList(player:GetStartingPlot(), wonderSlot.minDistance, wonderSlot.maxDistance, wonderSlot.placementType) do
			if WonderIsPossible(plot, placement, wonderSlot) then
				PlaceWonder(plot, placement, wonderSlot);
				count = count - 1;							
				if count < 1 then
					break;
				end
			end
		end
	end
end

On another note. I then handle custom wonders in WonderIsPossible and PlaceWonder.
 
Back
Top Bottom