Modders Guide to FfH2

Hello, I have several questions for regular FfH:

Is this correct that caster.setHasCasted(True) is not working in python? I tried to use this and unable to make my mage has casted. newUnit.setHasCasted(True) is working just fine.

Is there any way to remove free promotion from newly-created adept/mage/archmage AFTER they've been created? I tried newUnit.changeFreePromotionPick(0) and it would not work.

IIRC, in previous version, there is a +4 diplo modifier on "You have the patriarch." How can I reactivate this via XML/python?

Thanks!
 
You would have to change the promotion pick with a negative number, a change(0) means to do nothing.

Where in python are you trying to set the caster as having cast? It is possible that shortly after you are doing that other effects in the DLL are clearing the hasCast state from the unit (like if you are doing this to a summoned unit upon creation, or at the start of each turn to a unit)
 
Forewarning : sorry for the long rant. I just took a whole night to develop this spell and it just couldn't work :mad:

I tried to give Mind III new spell : Feeblemind. Basically it gives enemy units in range PROMOTION_FEEBLEMIND.

Then, each turn, PROMOTION_FEEBLEMIND has a PyPerTurn effectSilence. I think the line would be so easy, such as

Code:
def effectSilence
[INDENT]caster.setHasCasted(True)[/INDENT]

But this didn't do anything. I tried to move the command to a spell. So now each turn a unit with PROMOTION_FEEBLEMIND (pUnit) will cast a Silence spell. But it didn't work either.

In the end, I made spellSilence to create a newUnit, convert(pUnit) it and then pUnit.kill. After, I set newUnit.setHasCasted(true). This time, it works. If a vampire is cursed with PROMOTION_FEEBLEMIND, he will start his turn without the ability to cast.

But then, I found another problem : everytime an adept/mage/archmage is killed and a newUnit is created to replace them by this mechanism, the newUnit started with a free promotion. I tried to change the newUnit.changeFreePromotion(0) and (-1) but it doesn't work either. The result is, while having FEEBLEMIND, the unit is recreated and killed endlessly, and granted a new promo in each turn. Instead of curse, this spell has become a blessing! :cry:

Now, I randomly delete a magic promotion from the newUnit if it is an adept/mage/archmage. Thus, the free promotion can be used to bought the lost one back. Of course, this bring me to a new set of problem : if I forgot to spend the free promotion this turn, I will lost TWO promotion (losing a promotion each turn) while I just have ONE free promotion. That would be much worse if I forgot to spend the free promotion for several turn, as I would lost many promo, but that effect is intended for another spell :lol:

So, bottomline, if there is any easy way to use caster.setHasCasted(True) in CvSpellInterface.py, I would be gladly use it.

Or to remove a free promotion in same file.

Thanks!
 
convert(pUnit) should automatically kill pUnit for you. No need to run your own kill command.

And yes, the issue you are facing is that you are doing a "per turn" effect, which is firing BEFORE the spellcasting is restored for each unit. You would need to re-arrange the order of operations in the DLL to avoid that.

Can't promotions adjust the Miscast chance in base FfH? Just apply a +500% chance to Miscast on your Feeblemind instead of a block against casting.
 
The way you have it written def effectSilence has no parameters. You call on caster without ever defining the variable, so the game has no idea what unit it supposed to lose the ability to cast that turn. That code should work if you changed def effectSilence to def effectSilence(caster):
 
in gameutils.py, row 1373 there is an inactive code that adds a message what tech the the ai chooses.
Spoiler :
CyInterface().addMessage(0,true,25,"This is Player %s: best Research is (%i)" %(pPlayer.getName(),iBestTech),'',0,'',ColorTypes(11), 0, 0, True,True)

I have activated it to help me code and plan to copy-modify it to more places.

But it only gives me the indexnumber of the tech, not the name, and its timeconsuming to translating it. Is there someway to make it print out the techname instead?
 
The way you have it written def effectSilence has no parameters. You call on caster without ever defining the variable, so the game has no idea what unit it supposed to lose the ability to cast that turn. That code should work if you changed def effectSilence to def effectSilence(caster):

I forgot to add the parameter here, but I did include the parameter on the py file :D

convert(pUnit) should automatically kill pUnit for you. No need to run your own kill command.

Oh, I did not know that. Thank you.

And yes, the issue you are facing is that you are doing a "per turn" effect, which is firing BEFORE the spellcasting is restored for each unit. You would need to re-arrange the order of operations in the DLL to avoid that.

Aaahh.. That answer my problem. Modding DLL is beyond my capacity at the point. So, I guess I have to satisfy with current solution :crazyeye:

Here is my final code :

Code:
def effectSilenced(caster):
	caster.cast(gc.getInfoTypeForString('SPELL_SILENCED'))

def spellSilenced(caster):
	pPlayer = gc.getPlayer(caster.getOwner())
	pPlot = caster.plot()
	iAdept = gc.getInfoTypeForString('UNITCOMBAT_ADEPT')
	iSpellstaff = gc.getInfoTypeForString('PROMOTION_SPELLSTAFF')
	iSophiaFull = gc.getInfoTypeForString('PROMOTION_RING_OF_SOPHIA')
	iSophiaEmpty = gc.getInfoTypeForString('PROMOTION_RING_OF_SOPHIA_EMPTY')
	lList = []
	promotions = [ 'PROMOTION_AIR1','PROMOTION_BODY1','PROMOTION_CHAOS1','PROMOTION_DEATH1','PROMOTION_EARTH1','PROMOTION_ENCHANTMENT1','PROMOTION_ENTROPY1','PROMOTION_FIRE1','PROMOTION_LAW1','PROMOTION_LIFE1','PROMOTION_MIND1','PROMOTION_NATURE1','PROMOTION_SHADOW1','PROMOTION_SPIRIT1','PROMOTION_SUN1','PROMOTION_WATER1','PROMOTION_AIR2','PROMOTION_BODY2','PROMOTION_CHAOS2','PROMOTION_DEATH2','PROMOTION_EARTH2','PROMOTION_ENCHANTMENT2','PROMOTION_ENTROPY2','PROMOTION_FIRE2','PROMOTION_LAW2','PROMOTION_LIFE2','PROMOTION_MIND2','PROMOTION_NATURE2','PROMOTION_SHADOW2','PROMOTION_SPIRIT2','PROMOTION_SUN2','PROMOTION_WATER2']
	newUnit = pPlayer.initUnit(caster.getUnitType(), caster.getX(), caster.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
	newUnit.convert(caster)
	if newUnit.isHasPromotion(iSpellstaff):
		newUnit.setHasPromotion(iSpellstaff, False)
	if newUnit.isHasPromotion(iSophiaFull):
		newUnit.setHasPromotion(iSophiaFull, False)
		newUnit.setHasPromotion(iSophiaEmpty, True)
	if newUnit.getUnitCombatType() == iAdept:
		for i in promotions:
			if newUnit.isHasPromotion(gc.getInfoTypeForString(i)):
				lList = lList + [gc.getInfoTypeForString(i)]
		newUnit.setHasPromotion(lList[CyGame().getSorenRandNum(len(lList), "Destroy Magic")-1], False)
	newUnit.setHasCasted(True)
	CyEngine().triggerEffect(gc.getInfoTypeForString('EFFECT_PIT_BEAST_SUMMON'), pPlot.getPoint())

That is a little bit messy, but I couldn't find any other way to set a unit unable to cast at the beginning of its turn. Neither caster.setHasCasted(true) or pUnit.setHasCasted(true) worked, so... create new unit, clone it, and kill the original. THEN, I apply newUnit.setHasCasted(true). Messy, but working :lol:

Can't promotions adjust the Miscast chance in base FfH? Just apply a +500% chance to Miscast on your Feeblemind instead of a block against casting.

IIRC, vanilla FfH Promotion schema doesnt have a <iMiscast> tag.

Thank you, guys.
 
what you could do is create a useless spell and then force the unit to autocast that spell in the pyperturn of your promotion :lol: But unforunatly I think the autocast system isn't exposed to python.

Couldn't you just let the silent promotion remove the channeling1 promotion?

for the diplo modifier you need to modifiy the DLL, it is somewhere in CvPlayerAI
 
Or you could have the promotion grant access to a 'spell', which does nothing... Instead, it has a pyReq which sets the caster to be unable to cast. As spell requirements are ran every time a unit is selected, it would be rather difficult to get the unit to cast a spell so long as it has the promotion. ;)

And yes, doing it in a pyReq is bad form... But works. Magister did it for Auric, in order to let him automatically freeze terrain... TC01 copied it for the Frozen, which means it is in RifE. Works without any issues, as far as I'm able to tell. ;)



As Sephi said, the diplomodifiers are in DLL. The Patriarch modifier was removed, I believe.
 
Or you could have the promotion grant access to a 'spell', which does nothing... Instead, it has a pyReq which sets the caster to be unable to cast. As spell requirements are ran every time a unit is selected, it would be rather difficult to get the unit to cast a spell so long as it has the promotion. ;)

And yes, doing it in a pyReq is bad form... But works. Magister did it for Auric, in order to let him automatically freeze terrain... TC01 copied it for the Frozen, which means it is in RifE. Works without any issues, as far as I'm able to tell. ;)

Does that means something like this :

Code:
def reqSilenced(caster):
	caster.SetHasCasted(true)

and I put it in Pyreq of SPELL_SILENCE will actually remove the unit ability to cast? :eek: Never thought it before! Thanks, I'll try it.

As Sephi said, the diplomodifiers are in DLL. The Patriarch modifier was removed, I believe.

Too bad. Well I guess I have to satisfy with Patriarch without a diplo + then... :(
 
Does that means something like this :

Code:
def reqSilenced(caster):
	caster.SetHasCasted(true)

and I put it in Pyreq of SPELL_SILENCE will actually remove the unit ability to cast? :eek: Never thought it before! Thanks, I'll try it.

It should, yes. I honestly haven't used it in any of my OWN code before, but I've seen how others have so I'm fairly sure it will work here. Let me know how it goes. ;)

Normally I'd say to not use it, as this code is ran EVERY time the unit is selected... But in this case, with a simple one-line effect, I don't think it's too intensive.
 
It wouldn't quite be every time the unit is selected. If he doesn't give the spell <bIgnoreHasCasted>1</bIgnoreHasCasted> like I did Snowfall_passive then it will only run the first time each turn that it is selected, or right after casting Break Spellstaff or Consume Soul.


The python prereq way is what I'd suggest. Of course, the first s in SetHasCasted(true) should not be capitalized.
 
more python questions:
what does this mean?
- if pCity.findBaseYieldRateRank(YieldTypes.YIELD_PRODUCTION)>3
- if pCity.getBuildingProduction(eBuilding)==0:
- if pPlayer.canResearch(newtech,False):

is there any pythonfunction that test if a civ has acces to a resourse? (impacts how some techs, buildings and units should be valued in my mind, i am want to test working with it)

is there any pythonfunction that test if a civ has any specified unworked bonuses wihtin its borders? (if not just in a ballpark how hard would it be to construct one?)

Hope you don't mind me asking, in return i hope i can come up with some improvements to the AI that i can share.
 
pCity.findBaseYieldRateRank(YieldTypes.YIELD_PRODUCTION) return how good the Production of a City is relative to other cities. lower value is better production

pPlayer.canResearch returns true if player can research the newtech. (if you open techscreen F6 techs you can't research are shown in red (mostly religion techs))

to check for resources use for example
pPlayer.getNumAvailableBonuses(gc.getInfoTypeForString('BONUS_FINE_CLOTHES'))

the pythonfunction for any specified unworked bonuses is possible, but doesn't exist so far.
 
- if pCity.getBuildingProduction(eBuilding)==0:
Since Sephi seems to have missed this one, this just checks whether there are already some hammers invested into the eBuilding. Useful if you use blocks in the canconstructfunction, so that cities aren't forced to stop producing a nearfinished wonder if they fall in productionrank.

is there any pythonfunction that test if a civ has any specified unworked bonuses wihtin its borders? (if not just in a ballpark how hard would it be to construct one?)

Afaik you have to loop over all plots for this: In my mod I used this to determine whether a civ has a copper resource and should research bronze working:
Spoiler :
Code:
		if eTeam.isHasTech(gc.getInfoTypeForString('TECH_BRONZE_WORKING')) == False:
			if eTeam.isHasTech(gc.getInfoTypeForString('TECH_MINING')):
				if eTeam.getAtWarCount(True) >= 1:
					bhascopper=0
					for i in range (CyMap().numPlots()):
						pPlot = CyMap().plotByIndex(i)
						if pPlot.isOwned():
							if pPlot.getOwner() == ePlayer:
								eBonus = pPlot.getBonusType(TeamTypes.NO_TEAM)
								if eBonus == gc.getInfoTypeForString('BONUS_COPPER') :
									bhascopper=1
					if bhascopper==1:
						iTech = gc.getInfoTypeForString('TECH_BRONZE_WORKING')
 
Back
Top Bottom