How to Force a Deal?

sman1975

Emperor
Joined
Aug 27, 2016
Messages
1,376
Location
Dallas, TX
Am working on a mod that really needs the ability to force an 'Open Borders' and/or a 'Peace Treaty' (of more than a turn) between two civs. Have researched this a lot, but all roads seem to lead to a dead end.

I did find one thing that looked interesting:
The duration is part of the deal mechanism (via CvTeam:: setForcePeace()), so you'll have to force a deal between the two players containing the peace treaty


https://forums.civfanatics.com/threads/lua-how-do-you-force-a-peace-treaty.531720/#post-13383171


Since it's from @WHoward, I'm assuming it's good advice, but so far, I haven't been able to find a mechanism (in Lua) that can actually make a trade happen. Is this something that can only be done within a DLL?

Does anyone have any suggestions? Realize it's a long-shot. Thanks!
 
Should look at Sukritact's E&D Hun Code. I believe their decision forces a peace treaty...
 
Thanks. @Enginseer - looks like when they want to force peace, they use something like this:

1. Use the 'MakePeace' method to force peace between the two Teams.
2. Use to 'SetPermanentWarPeace' to TRUE to prevent an immediate DoW
3. Save the game turn this occurred into a persistent table.

4. Over subsequent turns, check to see if it's time to "cancel" the treaty. Once the turn comes along that is past the "peace duration" setting for this game speed, they then SetPermanentWarPeace to FALSE - thereby "cancelling" the treaty.

This is fairly brute-force, but the good thing you can say about it is - it works! :)

The situation I'm using this for is a WW1 scenario - when one civ is getting royally beaten (think Russia) - they want to sue for peace, and cease all hostilities. Without using logic like above, the initial 'MakePeace' worked, but other civs could immediately redeclare war the same or later turns. So, I now can figure out how to prevent this using this design.

One problem I'm still thinking through - when that civ sued for peace, it was because they were losing lots of cities, and foreign armies are crawling all over their territory. When the suit for peace hits, the two civs are now at peace, but the winning civ has units all over the losing civs territory. If war was not redeclared, they are frozen in place, unable to move.

The only way I was able to play through this was to obtain an Open Borders agreement with the losing civ - often at a very high price. This, of course, seem backwards to the way things like this should really work - the winning side would be able to dictate terms of withdrawal, not the other way around.

Anyways - thanks for the tip - it solved the first half of the problem well!
 
Scan through the units of all players on both sides. If a given unit is in the territory of a player they are not at war with and do not have open borders with, 'jump' the unit to a new location with Unit:JumpToNearestValidPlot().
Code:
  <api object="Player" method="IsPlayerHasOpenBorders">
      <arg pos="2" type="PlayerTypes" name="ePlayer"/>
      <ret type="bool"/>
  </api>
ie:
Code:
for iPlayer = 0, 62 do
	local pPlayer = Players[iPlayer]
	if (pPlayer ~= nil) and pPlayer:IsAlive() then
		local pTeam = Teams[pPlayer:GetTeam()]
		for pUnit in pPlayer:Units() do
			local iPlotOwner = pUnit:GetPlot():GetOwner()
			if (iPlotOwner ~= -1) and (iPlotOwner ~= iPlayer) then
				local iPlotOwnerTeamId = Players[iPlotOwner]:GetTeam()
				if (pTeam:IsAtWar(iPlotOwnerTeamId) == false) and (Players[iPlotOwner]:IsPlayerHasOpenBorders(iPlayer) == false) then
					pUnit:JumpToNearestValidPlot()
				end
			end
		end
	end
end
You may need to reverse the logic here
Code:
Players[iPlotOwner]:IsPlayerHasOpenBorders(iPlayer) == false
and use
Code:
pPlayer:IsPlayerHasOpenBorders(iPlotOwner) == false
I can't remember off the top of my head for certain whether the "giver" for the open-borders has to be the leading object or whether the "reciever" of the right to enter the territory has to be the leading object for the method.
 
Wow, thanks @LeeS - that looks like it could work. Finally found a good use for JumpToNearestValidPlot? :lol:

I think between yours and @Enginseer's recommendations, I can put together a working "sue for peace" function.


P.s. I'm also using your UnitSpawnHandler in the latest version of the mod - it's working out beautifully! Thanks for that as well.
 
Thanks, @WHoward, but you already taught me how to check for OB last week. :)

What I thought I wanted was a way to "force" OB on two civs, whether they like it or not... :crazyeye:

Having looked around quite a bit, I couldn't find any "elegant" (meaning a line or two of LUA) to accomplish this. But, I do think that what @LeeS offered up earlier will simulate it well enough.

It's my own fault, really, as I asked the wrong question initially. All I actually want to do is get (formerly) enemy units to vacate a civ's territory immediately after I've forced them to make peace. Maybe using that pesky JumpToNearestValidPlot could actually be well-suited for this?
 
Code:
for iPlayer = 0, 62 do
	local pPlayer = Players[iPlayer]
	if (pPlayer ~= nil) and pPlayer:IsAlive() then
		local iTeamID = pPlayer:GetTeam()
		local pTeam = Teams[iTeamID]
		for pUnit in pPlayer:Units() do
			local iPlotOwner = pUnit:GetPlot():GetOwner()
			if (iPlotOwner ~= -1) and (iPlotOwner ~= iPlayer) then
				local pPlotPlayer = Players[iPlotOwner]
				local iPlotOwnerTeamId = pPlotPlayer:GetTeam()
				local pPlotTeam = Teams[iPlotOwnerTeamId]
				if pPlotPlayer:IsMinorCiv() then
					--plot owner is a city-state
					if (pTeam:IsAtWar(iPlotOwnerTeamId) == false) and (pPlotPlayer:IsPlayerHasOpenBorders(iPlayer) == false) then
						pUnit:JumpToNearestValidPlot()
					end
				else
					--plot owner is a major
					if (pTeam:IsAtWar(iPlotOwnerTeamId) == false) and (pPlotTeam:IsAllowsOpenBordersToTeam(iTeamID) == false) then
						pUnit:JumpToNearestValidPlot()
					end
				end
			end
		end
	end
end
Corrected I sure hope based on William's note. You may have to still 'reverse' the player object/playerID setup in the minor civs part. I suppose it could be even more complicated by checking whether iPlayer for each player in the players table is a minor or major before doing anything further, but I think that would lead to four different conditional check 'set-ups'. But as coded shold give a rough idea as to where to start.

Heh. I even remember reading that other thread, and still my recollector did not function properly as to whether that player method would work for majors.
 
Actually the real complexity will be introduced by shifting aircraft carriers with planes on them. Because you need to not only shift the carrier, you need to shift the planes as well, and you need to shift the carrier first, then move the planes. But this requires you store a list of planes that are on the carrier, move the carrier, get its new XY location, and then move the planes to that new XY location.

Land units that are embarked when shifted will shift to the nearest land plot, but they will still look as embarked.

Sea units that are shifted can get shifted to a lake, which may or may not make them useless for the remainder of the game.

If you look at the complexity of the UnitSpawnHandler file the reason it is so complex is because of all these issues that the game simply does not handle for you when using JumpToNearestValidPlot, and the complexities required to spawn a unit onto the map at a given pre-determined XY location while at the same time not thereby deleting an enemy unit or giving a city to a player who is at war with the unit being spawned into the map.
 
Last edited:
@LeeS - you always offer such safe code suggestions! I noticed that when I looked at the UnitSpawnHandler. I used it to replace my own in the mod, because mine dealt with trapping about 5 different situations, while yours hit about 20. It seemed like a very good idea to make the swap...

I didn't test ships caught in an erstwhile enemy's tile when peace was forced - to see if they could move or not. Land units definitely could not. If ships can still move after MakePeace fires, I'm done.

But if they are also frozen, obviously special care will be needed to manage their redeployment. The complexities you pointed out about boat/carriers got me thinking:

Maybe I could use a "Get Nearest City" function like this one: https://forums.civfanatics.com/threads/lua-question-get-nearest-city-to-unit.565247/#post-14220577

Then replace the distance test with something like this:

Code:
if ( iDist < iShortestDistance ) and ( pCity:IsCoastal(???) ) then

Which would find the nearest coastal city - then SetXY for the ship to the city's plot. Can't decide if it's a good idea to use the dreaded JumpToNearestValidPlot from there? It's tempting to rewrite the UnitSpawnHandler as a UnitJumpHandler...

I'm not entirely sure what 'minWaterSize' in the IsCoastal method does. Will need to play around with that to see what the number really means.

Thanks for the code - it looks quite good!
 
The minWaterSize argument in the IsCoastal(minWaterSize) method is for the same purpose as column MinAreaSize in buildings and units. It is best to grab the data for GameDefines.LAKE_MAX_AREA_SIZE and add +1 to it. This ensures that the city will not be seen as 'coastal' when it is actually adjacent to a lake.

The standard unmodded value for LAKE_MAX_AREA_SIZE is '9'. Lighthouses, Seaports, Great Lighthouse, etc., use a MinAreaSize value of '10'.
 
Back
Top Bottom