Control placement of specific city states on a random map?

Pazyryk

Deity
Joined
Jun 13, 2008
Messages
3,584
I can see in AssignStartingPlots.lua where the starting plot is assigned to city states by playerIDs. But where is the MinorCivilizations type/id (e.g., MINOR_CIV_WARSAW) assigned to a specific playerID (or starting plot)?

I need to control this in my mod. Not for a WB map. What I need is some Lua logic to place CSs of certain MinorCivTrait in certain places in a random map. Surely there is a way... (Lua method preferred, even if it is a kludge.)
 
it's already assigned when AssignStartingPlots.lua is called, try to add a print statement with Players[playerID]:GetName() for example.
 
So this assignment happens after GameSetupScreen.lua (where I can still change number) but before AssignignStartingPlots.lua? And I have no access to the actual selection in Lua?

Oh well, looks like I can't pick these. But I can look at what the engine picked already and assign starting plots accordingly. That will work well enough.
 
funny,that's more or less the subject of my last post here :)

selecting which CS you want to load from a large selection for a specific map is tricky, to say the least.

you can't assign them for a specific player ID, and I'm not even sure if it's doable using the DLL.
 
and I'm not even sure if it's doable using the DLL.

Neither am I. There is a large chunk of the game start up code "missing" - I can only assume it's in the "Game Engine Framework". The last thing we see in Lua is PreGame.SetNumMinorCivs() ... the next time we see the minors in AssignStartingPlots (at "local cityState = Players[city_state_ID];") the minor trait (maritime, cultured, etc) has already been assigned. I can find the method in the DLL that does the assignment but it is never called in any code we have access to - neither from C++ or Lua (the latter because the method isn't actually exposed).

Your best bet is probably to try and match the CS start locations with the CSes in play, as opposed to the current method in AssignStartingPlots that just (effectively) picks them at random.

Not sure how you actually want to control CS placement, but you could probably come up with a method that looped over the minors, finding all the maritime states and placing them on the coast, all the faith states and trying to get them near a natural wonder, dividing the military ones across all the continents, etc and then just randomly filling in with what's left.
 
OK, I think you've posted this somewhere before. But will it work if I:
  1. PreGame.SetNumMinorCivs(41) --in GameSetupScreen.lua (assuming I have >= 41 CSs in MinorCivilizations)
  2. rewrite AssignignStartingPlots.lua to only assign starting plots for the ones I want?

If a playerID never gets a starting plot, then they have no units and are not alive and won't affect the game? Correct?

It's a bit complicated to explain what I'm trying to do. Basically, I have a reserved CS type (with a special MinorCivTrait) that I need to place myself. The number of other CSs should match map default (or player selection in advanced) and can be placed normally.
 
Your method should work, and IIRC there are some cases when AssignStartingPlots already discards city states that it can't place (too many of them, not enough starting position left)
 
This seems to work. Still needs some user-friendliness code but it shows the basic idea if anyone else is interested. For testing/experimentation, I wanted to see if I could get the game to only place cultural city states. It worked!

In GameStartupScreen.lua, add this in OnStart():
Code:
PreGame.SetNumMinorCivs(41)
--What we really need to do is GetNumMinorCivs first and save it away for use below, but I haven't gotten around to that yet.

Everything else in AssignStartingPlots.lua (or a file included from there). First, you need a function to get the city state you want from the 41 randomized ones that the game engine has prepared for you:
Code:
--debug: let's try only allowing cultural city states
local nextCityStateID = GameDefines.MAX_MAJOR_CIVS - 1
function GetNextCityStateID()
	while nextCityStateID < GameDefines.MAX_PLAYERS - 2 do
		nextCityStateID = nextCityStateID + 1
		local cityState = Players[nextCityStateID]
		local trait = cityState:GetMinorCivTrait()
		print("GetNextCityStateID ", nextCityStateID, cityState:GetMinorCivType(), trait)
		if trait == GameInfoTypes.MINOR_TRAIT_CULTURED then
			print(" --found cultural city state; returning ID")
			return nextCityStateID
		end
	end
	error("GetNextCityStateID ran out of possible city states")
end

then, in about 4 or 5 places in AssignStartingPlots.lua, change one line as noted by "Paz" below:
Code:
local city_state_ID = GetNextCityStateID()	--Paz changed from: cs_number + GameDefines.MAX_MAJOR_CIVS - 1;
local cityState = Players[city_state_ID];
local cs_start_plot = Map.GetPlot(cs_x, cs_y)
cityState:SetStartingPlot(cs_start_plot)
Then you have to trick AssignStartingPlots into thinking there are only x number of city states:
Code:
self.iNumCivs, self.iNumCityStates, self.player_ID_list, self.bTeamGame, self.teams_with_major_civs, self.number_civs_per_team = GetPlayerAndTeamInfo()
--Paz add:  override city state number
self.iNumCityStates = 6		--ideally, this should be value from GameStartupScreen before we overrode it
--end Paz add
A little more work needed to get the "placed" city state number from the game start menus and to remove any possibility for the "run out of city states" error (a problem if you try to place 20+ cultural CSs).

Note: After this, all possible IDs for city states (22 - 62) will return true for player:IsEverAlive(). But only those placed on the map will give true for player:IsAlive().
 
First, you need a function to get the city state you want from the 41 randomized ones that the game engine has prepared for you

If you're looking for a specific CS, the only way to guarentee that it's been placed is to have no more than 41 minors defined.

Also, probably not an issue for your specific case, but does "killing off" the surplus CSes have any impact on Diplomatic victory conditions?
 
If a playerID never gets a starting plot, then they have no units and are not alive and won't affect the game? Correct?

I'm not sure about that script, but if a CS isn't given a city they do still get a settler. So next you'd have to run some code that finds the settler, creates a city, and then kills the settler.

Events.ActivePlayerTurnStart.Add ( convertCSsettlersToCities );

I haven't tested this..
Code:
function convertCSsettlersToCities  ()
	print("-I- begin convertCSsettlersToCities  " );
        for iPlayer = GameDefines.MAX_MAJOR_CIVS-1, GameDefines.MAX_CIV_PLAYERS-1, 1 do
		local pPlayer = Players[iPlayer];
		if (pPlayer:IsAlive()) then
			for pUnit in pPlayer:Units() do
                                x = pUnit.GetX()
                                y = pUnit.GetY()
                                pPlayer:InitCity($x,$y)
				pUnit:Kill();
			end
		end
	end
	print("-I- done with convertCSsettlersToCities  " );
end
 
Also, probably not an issue for your specific case, but does "killing off" the surplus CSes have any impact on Diplomatic victory conditions?
Not sure. I'd guess No because conquered CSs don't get a vote (right?) and the non-placed CSs are like conquered CSs (IsEverAlive=true; IsAlive=false).

I'm not sure about that script, but if a CS isn't given a city they do still get a settler.
No, they don't because they are never given a starting plot. My changes above make AssignStartingPlots skip them entirely. No starting plot = no settler. (Which leads to another question: Would the settler suddenly appear if you SetStartingPlot in mid-game?)


So next you'd have to run some code that finds the settler, creates a city, and then kills the settler.
Why? The settlers placed at their starging plots will settle as they normally do (unless you put them somewhere that they can't settle, but I haven't changed starting plot selection at all in the code above).


Note: My only worry is if there is some code that expects CS playerIDs to be sequential, which would then be broken. But I'm not aware of any. Usually Firaxis does a player:IsAlive() test on all playerIDs and an ID range test to see whether it is full or minor civ (or barb). This all works fine with discontinuous playerIDs.
 
My only worry is if there is some code that expects CS playerIDs to be sequential.

You answered your own question while answering mine at the top of the post :) The player IDs will be sequential, it's just that there's been an incredibly high mortality rate among the CSes ;)
 
Back
Top Bottom