Controlling visibility and FOW clearing from meeting/allying with CSs

Pazyryk

Deity
Joined
Jun 13, 2008
Messages
3,584
So I have some specific city states squirreled away where players can't meet them (at the south pole behind some mountains). They are "hidden minor civs". Players only meet them via Lua. Of course, this brings up dozens of issues, like removing many options from the UI (e.g., find on map, gift units, etc.), most of which are pretty straightforward.

The issues I'm feeling most unsure about are the city plot revealing (which happens automatically when you meet them) and city visibility for allied players. Is there any way to stop either or both of these by Lua?

If not, I could settle for undoing plot visibility after the fact (though that leaves the minimap resized which kind of sucks). I haven't worked with plot visibility or FOW so I would appreciate any heads-up for difficult issues...
 
Is there any way to stop either or both of these by Lua?

Not that I know of, or can find in the DLL

If not, I could settle for undoing plot visibility after the fact (though that leaves the minimap resized which kind of sucks). I haven't worked with plot visibility or FOW so I would appreciate any heads-up for difficult issues...

Untested, the following should hide a CS back in the fog, if there are resources that the CS will reveal you'll also need to make a minor mod to the ResourceIconManager to hide those again

Code:
directions = {DirectionTypes.DIRECTION_NORTHEAST, DirectionTypes.DIRECTION_EAST, DirectionTypes.DIRECTION_SOUTHEAST,
              DirectionTypes.DIRECTION_SOUTHWEST, DirectionTypes.DIRECTION_WEST, DirectionTypes.DIRECTION_NORTHWEST}

--
-- Hide a City State back in the fog from their current ally
-- Adapted from CvMinorCivAI::SetAlly()
--
function HideInFog(iCS)
  local iAllyPlayer = Players[iCS]:GetAlly()
  
  if (iAllyPlayer ~= -1) then
    local iAllyTeam = Players[iAllyPlayer]:GetTeam()
    local iPlotRange = GameDefines.PLOT_VISIBILITY_RANGE
  
    for iPlot = 0, Map.GetNumPlots()-1, 1 do
      local pPlot = Map.GetPlotByIndex(iPlot)
      
      if (pPlot:GetOwner() == iCS) then
        pPlot:ChangeAdjacentSight(iAllyTeam, iPlotRange, false, -1, -1, false) -- Magic values taken from CvMinorCivAI::SetAlly()
        pPlot:SetRevealed(iAllyTeam, false)
        
        -- Now any adjacent hexes, there really is no elegant way to do this, so just blitz all six
        -- This makes the major assumption that GameDefines.PLOT_VISIBILITY_RANGE is 1 (the default)
        -- If not, you'll need to use PlotAreaSpiralIterator()
        for iDirection = 0, #directions, 1 do
          local pAdjacentPlot = Map.PlotDirection(pPlot:GetX(), pPlot:GetY(), directions[iDirection]))
          
          -- Normally we're not so close to the map edges to care about pAdjacentPlot being nil, but we'll make an exception here!
          if pAdjacentPlot then
            pAdjacentPlot:SetRevealed(iAllyTeam, false)
          end
        end
      end
    end
    
    Map.UpdateDeferredFog()
  end
end

EDIT: Given that you'll know how many tiles are revealed by each CS you need to call this for, you'll be able to seriously optimise the loop that scans every tile on the map!
 
Thanks for the code!

I wonder if I can hook up to an Events.HexFOWStateChanged listener and do it plot-by-plot? Alternatively, I should be able to use a TeamMeet listener so I only have to deal with the CS that was just met (assuming the event fires after the plot revealing, which I'll have to test). I guess for allies I'll have to run the code every turn (unless HexFOWStateChanged works and I can shut off visibility every time the engine adds it).
 
As they are hidden behind mountains I'm assuming you're doing something like

pPlayerTeam:Meet(iCsTeam)
pCs:ChangeMinorCivFriendshipWithMajor(iPlayer, iLots)

to meet them and make them allies

you'll get the TeamMeet event after the first line and the terrain revealed after the second, so you just need to hide the CS back into the FoW after the second line.
 
pPlayerTeam:Meet(iCsTeam) to meet them. After that, the player can make them into friends or allies the normal way (I have to disable some inappropriate quests).

The visibility here is only inappropriate for human player. Seeing these shouldn't affect AI behavior because the plots are inaccessible. Or even if I do want to remove AI visibility, I don't need to be in a hurry about it. So I think I'll try a graphics only approach first:

Code:
local function ListenerHexFOWStateChanged(vector2, fowType, bWholeMap)
	if  vector2.y < 3 and fowType ~= 1 then	--hide southernmost 2 rows of map
		Events.HexFOWStateChanged(vector2, 1, false)
	end
end
Events.HexFOWStateChanged.Add(ListenerHexFOWStateChanged)
Not sure if HexFOWStateChanged can be invoked like that -- many Events can but some can't. If not I'll use pPlot:SetRevealed(iHumanTeam, false). I wonder if I'll even need Map.UpdateDeferredFog(). If I'm lucky, the fowType will be changed back before any graphic update occurs (but perhaps too late already since the Event is firing from the graphics engine). I just have to try it...
 
If not I'll use pPlot:SetRevealed(iHumanTeam, false). I wonder if I'll even need Map.UpdateDeferredFog()..

I've always had to pair SetRevealed() with UpdateFog() as sometimes even UpdateDeffereFog() doesn't seem to work (but that was a long time ago, things may have changed when hot-seat came out)
 
This works:

Code:
local function ListenerHexFOWStateChanged(vector2, fowType, bWholeMap)
	--print("ListenerHexFOWStateChanged ", vector2.x, vector2.y, fowType, bWholeMap)
	if  vector2.y < 3 and fowType ~= 0 then				--listener says off or not vis
		Events.HexFOWStateChanged(vector2, 1, false)	--invoker sets to unexplored (which will be 0 in resulting listener call)
	end
end
Events.HexFOWStateChanged.Add(ListenerHexFOWStateChanged)
The code above makes the southern 2 rows "unrevealable" (even if you are walking through them). It's a nice example of using Events to listen for and then affect graphics. There is one thing odd about this one that I had to learn through some testing. The fowType integer reported from the listener is always wrong, but in a predictable way.

actual fowTypes = 0 ("off" meaning visible); listener reports fowType = 2
actual fowTypes = 1 (unexplored in clouds); listener reports fowType = 0
actual fowTypes = 2 (explored but not visible); listener reports fowType = 1

So when you put a plot back in the clouds (evoke with fowTypes = 1) the graphic engine does it (it works!) but the listener immediately reports back fowType = 0. It also consistently reports wrong fow (as indicated in my table above) as you move a unit around the map.


Unfortunately, this does not affect the minimap. So now I have to figure that out (if it is even possible).
 
Unfortunately, this does not affect the minimap. So now I have to figure that out (if it is even possible).

For some of my mods, I tried trying to fake a "hot-seat player switch" (as that seems to be the only thing that clears and redraws the mini-map), but gave up as not worth the effort for my needs. Quick-save, quick-load is a much more efficient use of my time.
 
Back
Top Bottom