Modders Guide to FfH2

@Xienwolf: Not bad for someone, who says of himself NOT to be proficient at Python. ;)

The code looks good to me and should work nicely. The only thing, that bothers me is, that you are using your description string for the random number as a list index later on. You'll have to save the number to a variable first.

Ok, here is the slightly modified code I would use:

Code:
	listPlayers = []
	pPlayer = gc.getPlayer(pUnit.getOwner())
	for iPlayer in range(gc.getMAX_PLAYERS()):
		pOtherPlayer = gc.getPlayer(iPlayer)
		eOtherTeam = TeamTypes(pOtherPlayer.getTeam())
		if (pOtherPlayer.isAlive()):
			if pPlayer.isHasMet(eOtherTeam):
				listPlayers.append(pOtherPlayer)

	iGift = CyGame().getSorenRandNum(len(listPlayers) * 2, "Gift")
	if iGift <= len(listPlayers):
		pCity = listPlayers[iGift].getCapitalCity()
	else:
		pCity = pPlayer.getCapitalCity()
	newUnit = listPlayers[Gift].initUnit(gc.getInfoTypeForString('UNIT_MIT0'), pCity.getX(), pCity.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
 
Yikes. I'm very glad I spend more time trying to figure that out for myself - Much more advanced than anything I've done. I'll go look up what "len" and the [] are doing...

Thanks oodles, guys! :)
 
Oh, I thought it stored the result in that description string :) It's just there to explain why you are calling for a random then? See, not proficient :p

If not proficient, then certainly very potent. Ask around how many people playing civ know what Python is. Less than 90%, I believe. And a big part of those would tell you it's a snake. ;)
 
len is asking what the length of the list is. And [] means an empty list. if you wanted the list to have items in it initially you would instead use [Item0, Item1, Item2] to have a 3 item list. The XXX.append(yyy) command adds whatever is listed there to the end of the list mentioned.
 
Okay... my inattention for other matters while trying to work this out just resulted in a minor explosion - of the soda can in the freezer type - so I'm back for more help. :)

I want the unit to have a 50% chance of going to the Scions no matter who owned the unit when it is killed. The code above will give a 50% chance to whoever owns it when killed, yes? If so how do I change it?

I have to go now or something'll catch fire...


I like using the double sized-list for the 50% chance, btw. Nice trick.
 
I suppose you only want the unit to got to civs the Scions have met, not to civs the current owner has met? In this case you shouldn't put the scions in the final else statement. Put them in the second line instead like this:

Code:
pPlayer = gc.getPlayer(gc.getInfoTypeForString('CIVILIZATION _SCIONS'))

Furthermore it's advisable to add a -2 to the soren line like this:

Code:
iGift = CyGame().getSorenRandNum(len(listPlayers) * 2 - 2, "Gift")

to compensate for the fact, that the scions have probably met themselves and are therefore in the listPlayers list. Otherwise the chance of the unit going to the Scions will be slightly higher than 50%.
 
Thanks to both of you! Again something I wouldn't have figured out on my own for a coon's age. Approximately. I wasn't even aware you could declare a Player that way.

:worship:
 
Good point. It's not going to work this way. Sorry, sometimes I'm stupid. To find out which player is playing the Scions you'll actually have to do something like this:

Code:
pPlayerScions = None
iNumPlayers = gc.getGame().countCivPlayersEverAlive()
for iPlayer in range(iNumPlayers):
	pPlayer = gc.getPlayer(iPlayer)
	eCivType = pPlayer.getCivilizationType()
	if eCivType == gc.getInfoTypeForString('CIVILIZATION _SCIONS'):
		pPlayerScions = pPlayer

Complicated, I know, but there is no other way (I know of) to determine the player of a certain civilization.
 
Good point. It's not going to work this way. Sorry, sometimes I'm stupid. To find out which player is playing the Scions you'll actually have to do something like this:

Code:
pPlayerScions = None
iNumPlayers = gc.getGame().countCivPlayersEverAlive()
for iPlayer in range(iNumPlayers):
	pPlayer = gc.getPlayer(iPlayer)
	eCivType = pPlayer.getCivilizationType()
	if eCivType == gc.getInfoTypeForString('CIVILIZATION _SCIONS'):
		pPlayerScions = pPlayer

Complicated, I know, but there is no other way (I know of) to determine the player of a certain civilization.

This may fail if multiple players are playing the Scions, it will only return the last one. You could do it with lists, and then get all the players who are playing the scions...

Code:
pPlayerScionsList = filter(lambda pPlayer : \
                           (pPlayer.getCivilizationType() == \
                              gc.getInfoTypeForString('CIVILIZATION_SCIONS')), \
                           [ gc.getPlayer(iPlayer) for iPlayer in \
                               range(gc.getGame().countCivPlayersEverAlive()) ])

Quick explanation if you haven't used filter or lambdas...

filter looks at a list and calls a function on each item in the list if that function returns false, that item is not included in the returned list.

lambda just creates a function on the fly, all my function is doing is returning a boolean if the current item's civ type is the scions.

You then specify the range as the player objects for all players in the game.

I tested it with the python editor in Shift+~ and it returns the list.
 
Progress report: Things are coming along thanks to the Forumites. A few quick and dirty tests and this seems to be working... at least if you try it just a few times under artificial conditions. I'm not sure I've eliminated a bug that destroys your capital...

I had some problems with the code so kindly provided -

Specifically:
In the first section the code won't run if the two commented lines are included (even when it is indented properly.) It's the lines designed to limit the civs selected to those contacted. No idea why it's not working. (I tried Xienwolf's version too - same result. Though I didn't test it enough to rule out a typo or some other minor error.

"newUnit = listPlayers[X]" didn't seem to work. Though, now that I think of it again, there may have been a typo: I might have been using [iGift] rather than [Gift].

Anyway, here's the result of my tampering. I added some message lines just to help me understand what was and wasn't running.

It is, I warn you, not pretty.

Spoiler :

Code:
def MIT0(pCaster):
	CyInterface().addImmediateMessage("one","")
	listPlayers = []
	pPlayer = gc.getPlayer(pCaster.getOwner())	
	for iPlayer in range(gc.getMAX_PLAYERS()):
		pOtherPlayer = gc.getPlayer(iPlayer)
#		eOtherTeam = TeamTypes(pOtherPlayer.getTeam())
		if (pOtherPlayer.isAlive()):
#			if pPlayer.isHasMet(eOtherTeam):
			listPlayers.append(pOtherPlayer)
				
	CyInterface().addImmediateMessage("two","")				
	iGift = CyGame().getSorenRandNum(len(listPlayers) * 2, "Gift")
	if iGift <= len(listPlayers):
		pCity = listPlayers[iGift].getCapitalCity()
		pPlayer = listPlayers[iGift]
		newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_MIT0'), pCity.getX(), pCity.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
		CyInterface().addImmediateMessage("toothercivend","")			
	else:
		CyInterface().addImmediateMessage("toscionstart","")	
		for iPlayer in range(gc.getMAX_PLAYERS()):
			pPlayer = gc.getPlayer(iPlayer)
			if (pPlayer.isAlive()):
				if pPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_SCIONS'):
					pCity = pPlayer.getCapitalCity()
					newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_MIT0'), pCity.getX(), pCity.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
                CyInterface().addImmediateMessage("toscionsend","")


This was all written before I saw westamastaflash's post, btw.
 
iHasMet needs to be called by a CyTeam object, not a CyPlayer object.

If you add
Code:
gc.getTeam(pPlayer.getTeam()).isHasMet(eOtherTeam)

It should work?
 
Question for anyone who may know - Is there a way to prevent "multiple casters" from casting a spell? My hire mercenary spell calls a screen, and that screen is only called once even if there are multiple casters. I'd like to make the spell so that it can only be cast if the single unit is the only one selected.

I think I want to get it out of the spell eventually, and add the ability to click a button "mercenaries" in the city view that would let you hire mercenaries from the city screen.
 
To keep a stack from casting I guess you could put a Python call to check that the groupsize is 1 for the caster.


Making it into a screen really wouldn't be too hard. Look at Teg Navanis's UnitStats mod (incorporated in FF) to see how he generates a new screen. Since you already have a screen designed all you should need is the parts in CvMainInterface.py which launch the screen itself, which is really quite simple.

To prevent using the screen more than 1 time per turn you might be able to tie in to the Feats system (Worldspells, Circumnavigation of the globe, Sirona's Touch healing....)
 
It should work?

Looks like it! Thanks!

Now, occasionally, a new unit isn't generated. The python is getting past the place where the Players list is assembled - I get the "two" message. But no further messages or activity. Any ideas? I'm going to try to pin it down to the exact line place it breaks down.

I'm also getting a CTD sometimes.
I think the crash may be connected to the python getting called more than once in a turn: I've been killing off the unit over and over (a couple of dozen trials) and it seems that if I hit the Turn button between each killing the CTD doesn't manifest. Maybe I need to make sure some variables are reset when the python is called? (That's an "uninformed guess", btw.)
 
To keep a stack from casting I guess you could put a Python call to check that the groupsize is 1 for the caster.

Thanks, I'll try that.

To prevent using the screen more than 1 time per turn you might be able to tie in to the Feats system (Worldspells, Circumnavigation of the globe, Sirona's Touch healing....)

I think I ought to make it either once / city / turn, or else just leave it open to as much as you can afford. I think i'll just build it so that you can hire a Mercenary company as much as you want for a city, since it costs gold to do so. I'll have to stop the Kahzad from using all their gold on them though.
 
It dawns on me that we are including the Barbarians in this list. They count as a player, and you most certainly have contacted them.

The Barbarians would be fine as a target-civ... but setting up a location for the unit to appear sounds problematical.

I added:

Spoiler :

Code:
		if (pOtherPlayer.isAlive()):
	[b]		if (pOtherPlayer != gc.getBARBARIAN_PLAYER()):[/b]
				if gc.getTeam(pPlayer.getTeam()).isHasMet(eOtherTeam):
					listPlayers.append(pOtherPlayer)


That look like it'll work? It's not giving me an error but I'm still getting the CTD or non-spawning sometimes. Though it may not be as often as before.
 
Back
Top Bottom