Unit spawning code only works first turn

Craig_Sutter

Deity
Joined
Aug 13, 2002
Messages
2,773
Location
Calgary, Canada
I am using the following code to generate units in city state cities. It appears to work fine on the first turn of the game (turn 1 in AD 500) and my print statements and looking at the result in game seems to bear this out.

However, a problem arises when a city state city is captured. Many of my city states have more than one city. If I set the year date at AD 501 to 504, for example, I will get a log message of "attempt to index local 'pCity' (a nil value)" in line 45 of the code (Highlighted in red). For some reason the city index gets screwed up by a conquest of one of the city state cities.

I have tried various means to correct this... used different if statements such as "neverconquered" or checking "isowner" and such, but none of them have made a difference. I've shuffled the order a bit by relocating the local variable statements withing the city enumeration code and have not moved forward. Except for occasionally getting no print statements at all (due to the logic of the "if" exclusions), I have failed to move forward to eliminate the error.

I've also tried to figure out alternate means of looping through the cities, thinking perhaps, that might get around problems accessing the database, but I can't figure out any other way to do it and scanning the game files, the method seems pretty standard.

I thought the problem is that for some reason the database is not updating for the conquests. Firetuner shows, though, that the city ownership changes via conquest have been changed in the database.

The only thing I can think of is that there might be some lua function to refresh the data on city ownership so the change circumstances can be accounted for... but non of the update functions I've found in lua seem to do the trick.

Anyhow, here is the code... it works, but only if no cities have been conquered. After conquests, it works until the loop reaches an affected city, then I get the error.

If I can't fix the error, the only way I can come up with is to treat each city state separately, with its own function, and live with the error being contained to only affected city states.

Here is the code:

Code:
local haveSpawnedMinorA = false

function SpawnMinorA()

if (haveSpawnedMinorA == false) then

	if (Game.GetGameTurnYear() >= 500 and Game.GetGameTurnYear() <= 502) then

    -- Set up MinorA Player

      haveSpawnedMinorA = true

		for iPlayer=GameDefines.MAX_MAJOR_CIVS, GameDefines.MAX_CIV_PLAYERS-1, 1 do 
        local pMinorA = Players[iPlayer]

		  MinorA = pMinorA  
			  
			  -- Enumerate cities
				local cityCount = -1
				
				for cityIndex = 0, MinorA:GetNumCities() - 1, 1 do
				
				cityCount = cityCount + 1
					
					if cityCount < (MinorA:GetNumCities()/2) then
					
					local pCity = MinorA:GetCityByID(cityIndex);
					[COLOR="Red"]local pPlot = pCity:GetCityIndexPlot();[/COLOR]
					local Spawnunit;
					local iSpawnX = pPlot:GetX();
					local iSpawnY = pPlot:GetY();
					Spawnunit = MinorA:InitUnit(GameInfoTypes["UNIT_SWORDSMAN"], iSpawnX, iSpawnY, UNITAI_ATTACK, DIRECTION_NORTHWEST );

					print (pCity:GetName() ,"...is spawning Swordsman... ");

					end
				end
			
		end
	end
end
end

Events.ActivePlayerTurnEnd.Add(SpawnMinorA)

I hope someone has run into something similar and can lead me in the right direction on this. Thanks.
 
try to put

Code:
local pCity = MinorA:GetCityByID(cityIndex);

before

Code:
if cityCount < (MinorA:GetNumCities()/2) then

I suppose next city index is not correct if you loop it before calling GetCityByID. Or something like that. IIRC city index is not just 0, 1, 2, 3...
 
This is fundamentally wrong

Code:
local pCity = MinorA:GetCityByID(cityIndex);

The ID of a city will be something like 8192, 32674 etc, not 0, 1, 2

[[EDIT: In my current game - City Name (City ID)

WorldView: Carthage (8192)
WorldView: Utique (16385)
WorldView: Hippo Regius (24578)
WorldView: Gades (32771)
WorldView: Saguntum (40964)
WorldView: Carthago Nova (49157)
WorldView: Panormus (57350)
WorldView: Lilybaeum (65543)
WorldView: Hadrumetum (73736)

]]

If you want to loop all the cities a player has the easiest (and correct) way is

Code:
for pCity in pPlayer:Cities() do
    // do what you want with the pCity object
end
 
If you want to loop all the cities a player has the easiest (and correct) way is

Code:
for pCity in pPlayer:Cities() do
    // do what you want with the pCity object
end

I totally agree :D

Still I'm wondering why the game use this (non-unique) id system and use the kind of code Craig_Sutter as taken as an example like in the medieval scenario:

Code:
function ConvertCatholicsNearby(pReformerCity)

	for iPlayer = 0, 46, 1 do
		local pPlayer = Players[iPlayer];
		if (pPlayer:IsAlive()) then
			for cityIndex = 0, pPlayer:GetNumCities() - 1, 1 do
    			local pCity = pPlayer:GetCityByID(cityIndex);
    			if (Map.PlotDistance(pReformerCity:GetX(), pReformerCity:GetY(), pCity:GetX(), pCity:GetY()) <= 10) then
					pCity:ConvertPercentFollowers(GameInfoTypes["RELIGION_PROTESTANTISM"], GameInfoTypes["RELIGION_CHRISTIANITY"], 40);
    			end
			end
		end
	end 
end
:confused:

it's also used in (rare) other part of the code, sometimes with a check for city ~= nil.
 
Still I'm wondering why the game use this (non-unique) id system and use the kind of code Craig_Sutter as taken as an example like in the medieval scenario:

My response to that question would get me banned (or at least suspended) from the forums for posting extreme obscenities :D
 
ok, thanks :D
 
Changing the city loop worked. Thank-you.

I, too, am wondering why the scenario files used the index numbers. Since going to the scenarios is one of my prime methods to figure out what to do to create my code, I'm a little surprised.

Anyhow, I've got to go through a lot of my codes that do not show errors, but do loop through the index, and change them to the method that you have given.

Thank-you again.
 
Mmmmm ... I'm wondering if World Builder assigns sequential IDs from 0 which is why it happens to work
 
Some investigation with FireTuner (below)

You can use Players[0]:GetCityByID(iIndex) to get cities, but it is useless as it leaves holes

Place 6 cities on the map, and then delete the 2nd and 3rd cities placed. GetNumCities() correctly returns 4, but the iIndex values you need are 0, 3, 4 and 5 (not 0 to GetNumCities()-1 as you would expect)

Code:
> Players[0]:GetNumCities()
6
 Runtime Error: [string "C:\Users\William\Documents\My Games\Sid Mei..."]:73: attempt to index local 'pCity' (a nil value)
 Runtime Error: [string "C:\Users\William\Documents\My Games\Sid Mei..."]:73: attempt to index local 'pCity' (a nil value)
> Players[0]:GetNumCities()
4
> Players[0]:GetCityByID(0):GetID()
8192
> Players[0]:GetCityByID(1):GetID()
40961
> Players[0]:GetCityByID(2):GetID()
Runtime Error: [string "_cmdr = {Players[0]:GetCityByID(2):GetID()}"]:1: attempt to index a nil value
stack traceback:
	[string "_cmdr = {Players[0]:GetCityByID(2):GetID()}"]:1: in main chunk
> Players[0]:GetCityByID(3):GetID()
Runtime Error: [string "_cmdr = {Players[0]:GetCityByID(3):GetID()}"]:1: attempt to index a nil value
stack traceback:
	[string "_cmdr = {Players[0]:GetCityByID(3):GetID()}"]:1: in main chunk
> Players[0]:GetCityByID(4):GetID()
49156
> Players[0]:GetCityByID(5):GetID()
57349

Unfortunately this just reinforces my (rather low) opinion of the devs
 
Back
Top Bottom