Mod-Modders Guide to Fall Further

Code:
def checkIsComplete(pCaster):
	pPlot = pCaster.plot()
	iBanung = gc.getInfoTypeForString('UNIT_BANUNG_THE_BLIND')
	iNga = gc.getInfoTypeForString('UNIT_NGA_EYE_EATER')
	if pCaster.getUnitType() == iNga:
		for i in range(pPlot.getNumUnits()):
			pUnit = pPlot.getUnit(i)
			if (pCaster.getOwner() == pUnit.getOwner and pUnit.getUnitType() == iBanung):
				pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_LONELY_NGA'), false)
				pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMPLETE_NGA'), true)
	if pCaster.getUnitType() == iBanung:
		for i in range(pPlot.getNumUnits()):
			pUnit = pPlot.getUnit(i)
			if (pCaster.getOwner() == pUnit.getOwner and pUnit.getUnitType() == iNga):
				pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_LONELY_BANUNG'), false)
				pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMPLETE_BANUNG'), true)

def checkIsLonely(pCaster):
	pPlot = pCaster.plot()
	iBanung = gc.getInfoTypeForString('UNIT_BANUNG_THE_BLIND')
	iNga = gc.getInfoTypeForString('UNIT_NGA_EYE_EATER')
	bLonely = true
	if pCaster.getUnitType() == iNga:
		for i in range(pPlot.getNumUnits()):
			pUnit = pPlot.getUnit(i)
			if (pCaster.getOwner() == pUnit.getOwner and pUnit.getUnitType() == iBanung):
				bLonely = false
		if bLonely == true:
			pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_LONELY_NGA'), true)
			pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMPLETE_NGA'), false)
	if pCaster.getUnitType() == iBanung:
		for i in range(pPlot.getNumUnits()):
			pUnit = pPlot.getUnit(i)
			if (pCaster.getOwner() == pUnit.getOwner and pUnit.getUnitType() == iNga):
				bLonely = false
		if bLonely == true:
			pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_LONELY_BANUNG'), true)
			pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMPLETE_BANUNG'), false)
The first function is tied to PROMOTION_LONELY: it checks every turn if one of the two unit is with the other. If so, it gives it a new promotion and removes the other. The second function is tied to the new promotion, PROMOTION_COMPLETE: it checks every turn if the two units are split. If so, it removes PROMOTION_COMPLETE and adds PROMOTION_LONELY. I've got no error in the logs and no change in game. Any idea why it isn't working? (And it's still for Orbis, so no UnitOnTile :p)
 
How about something like this?
Code:
animalunits=0
for pUnit in player.getUnitList():
     if pUnit.getUnitCombatType() == gc.getInfoTypeForString('UNITCOMBAT_ANIMAL'):
            animalunits+=1

That would work, but I really don't want to run a loop every turn. Honestly, I'm probably just going to leave the requirement as is, maybe up the number of units... By the time they have 40 units, they WILL have a few animals.

Problem is that this will run at LEAST once per turn per unit for the Doviello. At least until they cast their Worldspell for the first time, so unless they get 20 animals fairly early on, it'll be a fair slowdown for AI processing speed (though with the model designed I assume 20 animals WILL come fairly fast)

Actually, yes and no... The animal spawns slow down quickly as the city's population increases, so unless they REX it may take awhile. I could tie the spell to Lucian, but I don't want to do that either.

Couldn't it just be set to run once every 5 turns or so?

Oh, and I'd guess it'd be a good idea to only run the check if the No Animals game option isn't on.

Actually, the No Animals option has no effect on the Doviello Animal spawns... thought about putting in a check, but decided against it.
 
Number 1 best friend when code looks like it SHOULD work, but apparently isn't is text output.

For python use something like:

szNewFilename = "Debugging.txt"
pFile = open(szNewFilename, "w")
pFile.write("In CivScreen \n\n iCiv = %d \n Playable = %d \n AI Play = %d \n\n" % (iCiv, bAIPlayable, bPlayable))
pFile.close()


This will let you output text to a file.

Or

message = "In CivScreen \n\n iCiv = %d \n Playable = %d \n AI Play = %d \n\n" % (iCiv, bAIPlayable, bPlayable)
CyInterface().addMessage(CyGame().getActivePlayer(),True,25,message,None,0,None,ColorTypes(-1),0,0,True,True)


Plugging whatever output you want to see information for in the message variable. In this case, I would output the number for each unit once assigned, so I can check that is working right (one comes out -1 it means you have a typo somewhere in the unit types)

Seems to me there should be a more efficient way to run this check, but it shouldn't matter too much since you'll not often be in too large of a stack (hopefully) with either unit. Anyway, things look like they ought to work, so see if you get a text output at each IF step along the way to verify it is actually reaching that point. Might be something else wrong (like setting the promotion to MustMaintain and MinLevel -1 or something)
 
Code:
def checkIsComplete(pCaster):
	pPlot = pCaster.plot()
	iBanung = gc.getInfoTypeForString('UNIT_BANUNG_THE_BLIND')
	iNga = gc.getInfoTypeForString('UNIT_NGA_EYE_EATER')
	if pCaster.getUnitType() == iNga:
		for i in range(pPlot.getNumUnits()):
			pUnit = pPlot.getUnit(i)
			if (pCaster.getOwner() == pUnit.getOwner and pUnit.getUnitType() == iBanung):
				pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_LONELY_NGA'), false)
				pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMPLETE_NGA'), true)
	if pCaster.getUnitType() == iBanung:
		for i in range(pPlot.getNumUnits()):
			pUnit = pPlot.getUnit(i)
			if (pCaster.getOwner() == pUnit.getOwner and pUnit.getUnitType() == iNga):
				pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_LONELY_BANUNG'), false)
				pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMPLETE_BANUNG'), true)

def checkIsLonely(pCaster):
	pPlot = pCaster.plot()
	iBanung = gc.getInfoTypeForString('UNIT_BANUNG_THE_BLIND')
	iNga = gc.getInfoTypeForString('UNIT_NGA_EYE_EATER')
	bLonely = true
	if pCaster.getUnitType() == iNga:
		for i in range(pPlot.getNumUnits()):
			pUnit = pPlot.getUnit(i)
			if (pCaster.getOwner() == pUnit.getOwner and pUnit.getUnitType() == iBanung):
				bLonely = false
		if bLonely == true:
			pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_LONELY_NGA'), true)
			pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMPLETE_NGA'), false)
	if pCaster.getUnitType() == iBanung:
		for i in range(pPlot.getNumUnits()):
			pUnit = pPlot.getUnit(i)
			if (pCaster.getOwner() == pUnit.getOwner and pUnit.getUnitType() == iNga):
				bLonely = false
		if bLonely == true:
			pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_LONELY_BANUNG'), true)
			pCaster.setHasPromotion(gc.getInfoTypeForString('PROMOTION_COMPLETE_BANUNG'), false)

You have to use pUnit.getOwner()
 
Hi,

After creating a spell to build cities, I'm on a spell to create improvement (to tie it to a promotion). Actually, I created the spell and it works ok this times without much problems.

The only problem is that I would like the automated workers to use it. When I set the UNITAI_WORKER to the unit, it does have the "auto create improvement" action, and as it has the promo it also has the spell to create the improvement.

But I cannot find where to start to link the two... (Currently the worker does not have any Build option and so stay in the capital without using the spell to improve tiles)

Seems the Pirate Coves also are built through spells but I cannot seems to find how it is linked it to autoworkers.

Can you help ?
 
If you want to control unit AI, you really need to be in the DLL. There is some rudimentary control which can be had in python, but it is never quite as "smart" as what you can achieve in the DLL without sacrificing considerable processor speed. But to link to Automation (ie - worker for even humans) you MUST be in the DLL to pull it off at all (to the best of my knowledge, I am sure some of the python geniuses can find ways around that limitation, but honestly, the DLL is easier even if python is possible ;))

Relatively certain that the AI never builds Pirate Coves.
 
Thanks, Sephi and xienwolf, it's solved now. I fixed my getOwner() typo and took out the minLevel -1 tag (which I don't really understand I guess). Don't which did the trick, though. Thanks too for the debugging advice :goodjob:

If you want to share your more efficient way to check, I'm okay. Still learning python, so it's beginner work.
 
Main reason I was cryptic about it is that I don't have the time to sort it all out to be quicker, just a hunch that there is another way to go about it, then a realization that it won't matter too much in the end of everything anyway. Long as these functions are a PyPerTurn attached to the promotions directly, and not something that runs during every turn of every game after cycling through all of the units.


MinLevel of -1 is what you use to make it impossible to get a promotion when leveling up, but still have all of the other prereqs (mostly just valid unitcombats) so that it can be applied to a limited selection of units via spell or other python forcing. Unfortunately, MustMaintain absolutely requires that the promotion CAN be taken by normal level up methods (and typically AutoAcquire is used along with it so that you don't get the option to take it willfully). Of course, if you aren't using MustMaintain, that MinLevel -1 is perfectly fine, and standard in fact. Since these promotions are meant to be for specific units, I imagine you have them set to start with one or the other via UnitInfos <FreePromotions> tags. That bypasses all requirements on any promotion anyway.
 
MinLevel of -1 is what you use to make it impossible to get a promotion when leveling up, but still have all of the other prereqs (mostly just valid unitcombats) so that it can be applied to a limited selection of units via spell or other python forcing. Unfortunately, MustMaintain absolutely requires that the promotion CAN be taken by normal level up methods (and typically AutoAcquire is used along with it so that you don't get the option to take it willfully). Of course, if you aren't using MustMaintain, that MinLevel -1 is perfectly fine, and standard in fact. Since these promotions are meant to be for specific units, I imagine you have them set to start with one or the other via UnitInfos <FreePromotions> tags. That bypasses all requirements on any promotion anyway.
Okay. Hm. So this means that the issue was certainly the missing parenthesis. Thanks for the answers!
 
Python, in "def onUnitKilled", how do I get access to the unit used by attacker to kill unit? Specifically, I would like to get its name to make a neat message and to access its promotions to check some.

Here is the complete function:
Code:
# ngomele start			
			iNgozoleg = gc.getInfoTypeForString('BUILDING_NGOZOLEG')
			for iiX in range(iX-2, iX+3, 1):
				for iiY in range(iY-2, iY+3, 1):
					pPlot3 = CyMap().plot(iiX,iiY)
					if pPlot3.isCity():
						pCity = pPlot3.getPlotCity()
						if pCity.getNumRealBuilding(iNgozoleg) > 0:
							iBaseFood = 2
							CvUtil.pyPrint('Base food is %d'
								%(iBaseFood))
							iFood = iBaseFood + CyGame().getSorenRandNum(6, "Nga EyeEater")
							CvUtil.pyPrint('\nFood is now %d'
								%(iFood))
							pAttacker = pCity.getOwner()
							eTeam = gc.getTeam(pAttacker)
							if eTeam.isHasTech(gc.getInfoTypeForString('TECH_ANIMAL_HUSBANDRY')):
								iFood += 2
								iFood += CyGame().getSorenRandNum(5, "Ngomele")
								CvUtil.pyPrint('\nThanks to Animal Husbandry, food is now %d'
									%(iFood))
							if eTeam.isHasTech(gc.getInfoTypeForString('TECH_TRACKING')):
								iFood += CyGame().getSorenRandNum(5, "Ngomele")
								CvUtil.pyPrint('\nThanks to Tracking, food is now %d'
									%(iFood))
							#if iAttacker.isHasPromotion(gc.getInfoTypeForString('PROMOTION_NGOMELE_HUNTER')):
							#	iFood = iFood * 1.2
							if unit.getUnitCombatType() == gc.getInfoTypeForString('UNITCOMBAT_BEAST'):
								iFood = iFood * 2
								CvUtil.pyPrint('\nThis thing is huge! Food is now %d'
									%(iFood))
							if unit.getOwner() == pAttacker:
								if pCity.getNumRealBuilding(gc.getInfoTypeForString('BUILDING_DEADGNOLLS_COPSE')) > 0:
									iFood = iFood * 0.3
									CvUtil.pyPrint('\nWe have to bury them, food is now %d'
										%(iFood))
								else:
									iFood = iFood * 0.6
									CvUtil.pyPrint('\nThe copse awaits them! Food is now %d'
										%(iFood))
							pCity.changeFood(iFood)
							sCity = pCity.getName()
							sUnit = unit.getName()
							CyInterface().addMessage(pCity.getOwner(),True,25,CyTranslator().getText("TXT_KEY_MESSAGE_NGOMELE_HUNTING",(sUnit, iFood, sCity)),'AS2D_DISCOVERBONUS',1,'Art/Interface/Buttons/Buildings/Smokehouse.dds',ColorTypes(8),pCity.getX(),pCity.getY(),True,True)
# ngomele end
I made it so owned unit yields less food than enemy units but when a friendly unit dies, no food is added to the city even though the changes are working (checked thanks to the pyPrints. Here is what the PythonErr.log has to say:
Code:
Traceback (most recent call last):

  File "CvEventInterface", line 23, in onEvent

  File "CvCustomEventManager", line 75, in handleEvent

  File "CvCustomEventManager", line 86, in _handleDefaultEvent

  File "CvEventManager", line 2006, in onUnitKilled

ArgumentError: Python argument types in
    CyCity.changeFood(CyCity, float)
did not match C++ signature:
    changeFood(class CyCity {lvalue}, int)
ERR: Python function onEvent failed, module CvEventInterface
Traceback (most recent call last):
  File "CvEventInterface", line 23, in onEvent
  File "CvCustomEventManager", line 75, in handleEvent
  File "CvCustomEventManager", line 86, in _handleDefaultEvent
  File "CvEventManager", line 2007, in onUnitKilled
TypeError
: 
this constructor takes no arguments

ERR: Python function onEvent failed, module CvEventInterface
Hum, I haven't noticed this float thing before posting... Will check
that. Would this be caused by decimals? Is there a way to ensure that iFood is an integer?

Found it. Just had to replace:
Code:
								if pCity.getNumRealBuilding(gc.getInfoTypeForString('BUILDING_DEADGNOLLS_COPSE')) > 0:
									iFood = iFood * 0.3
									CvUtil.pyPrint('\nWe have to bury them, food is now %d'
										%(iFood))
								else:
									iFood = iFood * 0.6
									CvUtil.pyPrint('\nThe copse awaits them! Food is now %d'
										%(iFood))
By:
Code:
								if pCity.getNumRealBuilding(gc.getInfoTypeForString('BUILDING_DEADGNOLLS_COPSE')) > 0:
									iFood = int(iFood * 0.3)
									CvUtil.pyPrint('\nWe have to bury them, food is now %d'
										%(iFood))
								else:
									iFood = int(iFood * 0.6)
									CvUtil.pyPrint('\nThe copse awaits them! Food is now %d'
										%(iFood))
:goodjob:
 
Not familiar with python functions, so someone may correct this if I am wrong. But I am reasonably certain there is NO way to get Attacker info from OnUnitKilled, you have to be in onCombatLost to manage that one. Main reason is there are tons of ways to die other than combat.

To ensure you are an int, cast the value back to int when you are done.

iFood = (int)iFood

You could also use Ciel or Trunc functions to control how rounding is handled if needed.
 
def onCombatResult(self, argsList): gives both winner and looser, but is also triggered when the Looser does not die (not sure if also ranged attacks trigger onCombatResult but I think they do). If you use it, make sure USE_COMBAT_RESULT_CALLBACK has the correct value.
 
A few questions.

Contemplating making dragons rideable, via a promotion.

Is it possible for a promotion to add cargo space to a unit that previously had none?
And also, is it possible to restrict what unitcombats can fill that cargo space? I don't want cannons strapped to a dragon for instance.

On second thought, that might actually be fun...
 
Yes, you can add cargo space to a unit with a promotion, not sure if it WORKS that well without having the unit itself set up to deal with cargo though. You cannot control cargo allowed by promotion though, only by UnitInfos, and even then only by specialUnit or Domain I think.
 
So I'm trying to do something similar to the Khazad vaults with the Bannor. The primary difference is that the building granted to each Bannor city is dependent on what religions are in that city; if there's JUST your state religion, you get Solidarity of Belief. If there's a non-state religion, you get Lies and Heresy. If there's no religion at all in the city, you get Aimlessness. I'm using Dwarven Vaults as placeholders until I get the code figured out. In any case, the code doesn't seem to do anything in-game; I can start it up and everything, it's just I don't get the buildings.

I've put this in CustomFunctions, and I've put the doTurnBannor thing into the Event Manager. Is there something else I'm missing? (Entirely likely-- I don't know python terribly well, so I cobbled this together from Purge the Unfaithful and Dwarven Vaults.)
Code:
	def doTurnBannor(self, iPlayer):		
		pPlayer = gc.getPlayer(iPlayer)
		if pPlayer.getNumCities() > 0:
			iHeresy = gc.getInfoTypeForString('BUILDING_DWARVEN_VAULT_EMPTY')
			iNotHeresy = gc.getInfoTypeForString('BUILDING_DWARVEN_VAULT_LOW')	
			iNullHeresy = gc.getInfoTypeForString('BUILDING_DWARVEN_VAULT')
			for pyCity in PyPlayer(iPlayer).getCityList():
				pCity = pyCity.GetCy()
				iHeresyFactor = iNullHeresy
				StateBelief = PyPlayer.getStateReligion()
				for iTarget in range(gc.getNumReligionInfos()):
					if (StateBelief == iTarget and pCity.isHasReligion(iTarget)):
						iHeresyFactor = iNotHeresy
				for iTarget in range(gc.getNumReligionInfos()):
					if (StateBelief != iTarget and pCity.isHasReligion(iTarget)):
						iHeresyFactor = iHeresy
				pCity.setNumRealBuilding(iHeresy, 0)
				pCity.setNumRealBuilding(iNotHeresy, 0)	
				pCity.setNumRealBuilding(iNullHeresy, 0)				
				pCity.setNumRealBuilding(iHeresyFactor, 1)
 
Hope that helps
Code:
StateBelief = pPlayer.getStateReligion()
religioncount=0
for iTarget in range(gc.getNumReligionInfos()):
	if pCity.isHasReligion(iTarget)):
		religioncount+=1
if religioncount==0:
	pCity.setNumRealBuilding(iHeresy, 0)
	pCity.setNumRealBuilding(iNotHeresy, 0)	
	pCity.setNumRealBuilding(iNullHeresy, 1)
elif (religioncount==1 and pCity.isHasReligion(Statebelief)):
	pCity.setNumRealBuilding(iHeresy, 0)
	pCity.setNumRealBuilding(iNotHeresy, 1)	
	pCity.setNumRealBuilding(iNullHeresy, 0)
else:
	pCity.setNumRealBuilding(iHeresy, 1)
	pCity.setNumRealBuilding(iNotHeresy, 0)	
	pCity.setNumRealBuilding(iNullHeresy, 0)
 
Hope that helps

Thanks for the reply! Although unfortunately, there's still nothing happening when I actually do a game as the Bannor-- turns pass with OO in my city and I get neither the Heresy building or the NotHeresy building. So I've got doTurnBannor defined in CustomFunctions, and the command to doTurnBannor in the CvEventManager.... is there anything else I should need to get this to work?

EDIT: Unrelated, but what does pPlayer.getStateReligion() return as when the player doesn't have a state religion?
 
Back
Top Bottom