[MODCOMP] Mercenaries Mod

Give me about 1-2 weeks and I'll have a beta version ready for testing... I am working on moving a bunch of the code to the SDK since I am already touching it anyways. I'm trying to make the code a bit cleaner and more efficient than the python code that's already in place.
 
this is not a good news for me.
I would need to integrate the SDK code into mine, while all I needed was support for disabling it after the discovery of a certain tech (or an era, the same as the "Starting Era" setting in the INI); an easy way to integrate the mod component into mine; and future support for warlords.
 
Rhye said:
this is not a good news for me.
I would need to integrate the SDK code into mine, while all I needed was support for disabling it after the discovery of a certain tech (or an era, the same as the "Starting Era" setting in the INI); an easy way to integrate the mod component into mine; and future support for warlords.

Rhye, there is a list of the set of new features/enhancements/bugfixes/etc. that are going to be part of v0.8. You can find this list here:http://forums.civfanatics.com/showthread.php?t=173527
 
honestly, I don't see your excellent mod getting any better with all that stuff added. Implementing it and bugfixing will take a lot of time.
I'll stick with 0.6 for now for my mod, and I'll tweak the thing I need myself.

I hope the Warlords version won't take too long...
 
There's been a crash in WH that seems to come from the merc mod. I think this may be causing it, but not sure...

Code:
# Places the mercenary in the city specified by the objCity variable
	def place(self, objCity):
	
		# Return immediately if the mercenary is already in the game
		if(self.objUnit != None):
			return

		# get the player instance
		player = gc.getPlayer(self.iOwner)
		
		# return nothing if the player is an invalid value
		if(player == None):
			player = gc.getPlayer(self.iBuilder)
			if(player == None):
				return
			
		# return nothing if the player is dead
		if(player.isAlive() == false):
			return

		unitType = gc.getInfoTypeForString(self.objUnitInfo.getType())

		# Create the unit and place it in the game		
		[b]self.objUnit = player.initUnit(unitType, objCity.getX(), objCity.getY(), UnitAITypes.NO_UNITAI)[/b]

I've come up with the fact that the function is getting passed a city whose X and Y are -1, -1. Thus, apparently the unit is being init'd at -1, -1. Apparently, the AI bought a unit and something went ka-blammo?

I think there's a problem in the function used where it gets a random city. It gets a random number between 0 and the number of cities the player owns, and uses that number to get the city object using pPlayer.getCity(i). The problem is that I don't believe that that is how the getCity function works.

Say you create three cities (0, 1, 2). You now have 3 cities. Say city 1 gets knocked off. You're down to two cities, affectionately named 0 and 2. However, 2 doesn't "move over" to fill the gap in the city list, because it's stored in a fixed array. So, you have a city object in index 0 and 2, and a NULL in spot 1. Now, say you go through this bit of code:


MercenaryUtils.py
Spoiler :

Code:
def getMercenaryStartingLocation(self, iPlayer):
		' CyCity - the starting city for hired mercenaries'
		player = gc.getPlayer(iPlayer)
		
		objCity = None
		
		if(g_strMercenaryStartingLocation == "Capital City"):
			objCity = player.getCapitalCity()

		elif(g_strMercenaryStartingLocation == "Civilization Edge"):
			objCity = self.getCivilizationEdgeCity(iPlayer)

		elif(g_strMercenaryStartingLocation == "Random"):
			[b]objCity = player.getCity(gc.getGame().getMapRand().get(player.getNumCities(), "Random City"))[/b]
			

		elif(len(g_strMercenaryStartingLocation) > 0):
			objCity = self.getRandomCityWithBuildings(iPlayer)
			


		else:
			[b]objCity = player.getCity(gc.getGame().getMapRand().get(player.getNumCities(), "Random City"))[/b]
		
		if(objCity == None):
			[b]objCity = player.getCity(gc.getGame().getMapRand().get(player.getNumCities(), "Random City"))[/b]

		return objCity


In all of those places, you have a chance of getting back a city object that is actually invalid, because with 2 cities, your random number will either be 0 or 1. If it's one, you will call getCity(1), which will get you this code in the SDK:

Code:
CyCity* CyPlayer::getCity(int iID)
{
	return m_pPlayer ? new CyCity(m_pPlayer->getCity(iID)) : NULL;
}

I believe, but am not sure of, that what is actually returned is a new CyCity object, rather than returning None. Perhaps you would be better off taking a clone of the getRandomCityWithBuilding() function, but removing the element of searching for the building?

---

Edit:

Here is what I've changed that fixed the CTD in the test case I was using with WH, changes emboldened.

Code:
	# Returns the starting city for a player's mercenary
	def getMercenaryStartingLocation(self, iPlayer):
		' CyCity - the starting city for hired mercenaries'
		player = gc.getPlayer(iPlayer)
		
		objCity = None
		
		if(g_strMercenaryStartingLocation == "Capital City"):
			objCity = player.getCapitalCity()

		elif(g_strMercenaryStartingLocation == "Civilization Edge"):
			objCity = self.getCivilizationEdgeCity(iPlayer)

		elif(g_strMercenaryStartingLocation == "Random"):
                        [b]pCityList = PyPlayer(iPlayer).getCityList()
			objCity = pCityList[gc.getGame().getMapRand().get(len(pCityList), "Random City")].GetCy()[/b]

		elif(len(g_strMercenaryStartingLocation) > 0):
			objCity = self.getRandomCityWithBuildings(iPlayer)			

		else:
			[b]pCityList = PyPlayer(iPlayer).getCityList()
			objCity = pCityList[gc.getGame().getMapRand().get(len(pCityList), "Random City")].GetCy()[/b]
		
		if(objCity == None):
			[b]pCityList = PyPlayer(iPlayer).getCityList()
			objCity = pCityList[gc.getGame().getMapRand().get(len(pCityList), "Random City")].GetCy()[/b]

		return objCity
 
Thanks Gerikes for that fix...
 
Warhammer found another bug.

If a mercenary is hired by a civ, and the civ loses their last city before the mercenary arives, when the functions to place the mercenary goes through the list of cities will be length 0 and I don't think there are any checks for a civ with no city. I'm not sure what would happen to a Merc who lost it's last city but wasn't destroyed (the merc never arives, the merc is placed in a plot of one of the player's still-surviving units, etc.), otherwise, I would make a quick fix. I think this might take a little more understanding of the repurcussion. If the Merc never arrives, does it go back into the pool of Merc's available for higher? Should this be checked immediately, or only the turn that the merc would have arrived at. Etc. etc.
 
oh, i see gerikes is also into this thread :) anyway...theLopez, i found your mod is causing OOS in our modpack...disabling the mercs=no OOS, as soon as the mercs are enabled it causes OOS...now comes the tricky parts, it goes OOS on promoting units! just build units with a promotion=OOS any ideas?
 
mrgenie - It sounds like the OOS isn't because of the mercenaries mod since you are getting OOS when you build units with promotions. Try taking out the mercenaries mod and see if you are still getting OOS messages when building units with promotions, if you do then it isn't the mercenaries mod.
 
Hi thelopez..i thought it to be impossible first too...so i took out all the merc things...and loaded the savegame...could play without OOS 240turns tested...then i again copied all the pythons with the merc enabled..and i got the OOS again...tried it several times...either it's the merc mod..or an interaction with the merc mod...but without the merc mod i can play several times through the game of which the longest last 240turns..including the merc is OOS within 30-40turns :( i really have no idea..i looked over the code..i found some things where i raise a question mark..but i dont see the point why it should go OOS...i send my files to gerikes..maybe he can figure this one out..i hope
 
mrgenie said:
Hi thelopez..i thought it to be impossible first too...so i took out all the merc things...and loaded the savegame...could play without OOS 240turns tested...then i again copied all the pythons with the merc enabled..and i got the OOS again...tried it several times...either it's the merc mod..or an interaction with the merc mod...but without the merc mod i can play several times through the game of which the longest last 240turns..including the merc is OOS within 30-40turns :( i really have no idea..i looked over the code..i found some things where i raise a question mark..but i dont see the point why it should go OOS...i send my files to gerikes..maybe he can figure this one out..i hope

I can't actually run tests w/ mrgenie 'cuz I don't own WL, but in a game mrgenie had with someone else, they ran into an OOS error with their mod. Using a script to print out all the values that are checked by the OOS checksum between the two computers, we found this discrepancy:

Code:
1278c1278
< Player 1, Unit ID: 270344, Archer (EDU)
---
> Player 1, Unit ID: 253960, Archer (EDU)
1280c1280
< Damage: 0Experiecne: 3Level: 2
---
> Damage: 0Experiecne: 3Level: 1

Loading from the save file and trying again with mercmod enabled yields this difference, loading from the save file without the merc mod doesn't. I wasn't personally involved with the test so I can't say without a doubt that there wasn't some outside problem involved. However, I get the feeling that the ID difference might be due to the code that creates the merc temporarily using the active player. If I understand this correctly, if player 0 is the active player, the mod will create a unit for that player, including generating a new id for it. Then, the unit is deleted and it's info is added to the merc pool. When player 1 buys the merc, the new mercany is init'd now using player 1 for the initUnit. However, on player 1's computer this entire series of events happens with player 1 generating the ID both times. I don't think the actual code for how unit ID's are generated is available, so I'm not sure if this would or wouldn't have an effect (for example, the ID's of units generated might depend on the player they're generated for). This wouldn't explain the level difference, however.

I'm going to see with a default Civ4 game if that's the case. I'm also going to try setting up a test with just the merc mod that reproduces this affect mrgenie reported and see what happens.
 
Thanks Gerikes!


EDIT: Wait a minute... I haven't officially released a WL version... mrgenie, are you trying to use the merc mod in Warlords?
 
hello thelopez,

first me and mrgenie love all your addition - your doing a great job.

mrgenie have written your merc coe for warlords,
and it single game it works great.
buton mp game it causes oos...

mrgenie are trying to fix it with the help of gerikes.

he also fixed many things there.

:)
 
DO-NOT run non warlords code in walords....
 
Back
Top Bottom