Bug Reports and Discussion

end of turn hangs, will not complete. task manager at 0-1 % on the civ4 process, some kind of logic failure loop? game still responds otherwise. i looked in worldbuilder but didnt see anything unusual about to be completed anywhere. tried reloading 3 times on random seed no change. save attached
 

Attachments

end of turn hangs, will not complete. task manager at 0-1 % on the civ4 process, some kind of logic failure loop? game still responds otherwise. i looked in worldbuilder but didnt see anything unusual about to be completed anywhere. tried reloading 3 times on random seed no change. save attached

Unable to load the save. What version of MNAI are you using?
 
hey thanks for checking! i later noticed in my downloads folder a 2.53 alpha dll that had been posted(forgot about that one!), so maybe this may be part of my issue.. sorry for wasting troubleshooting time on this
 
In honor of my favorite unit, postCombatWolfRider should be like this:

Code:
def postCombatWolfRider(pCaster, pOpponent):
	if (pOpponent.getUnitType() == gc.getInfoTypeForString('UNIT_WOLF') or pOpponent.getUnitType() == gc.getInfoTypeForString('UNIT_WOLF_PACK')):
                pPlayer = gc.getPlayer(pCaster.getOwner())
                if pOpponent.plot().getNumVisibleEnemyDefenders(pCaster) > 1:
                        newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_WOLF_RIDER'), pCaster.getX(), pCaster.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
                        CyInterface().addMessage(pCaster.getOwner(),True,25,CyTranslator().getText("TXT_KEY_MESSAGE_WOLF_RIDER", ()),'',1,'Art/Interface/Buttons/Units/Wolf Rider.dds',ColorTypes(8),pCaster.getX(),pCaster.getY(),True,True)
		else:   
                        newUnit = pPlayer.initUnit(gc.getInfoTypeForString('UNIT_WOLF_RIDER'), pOpponent.getX(), pOpponent.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_SOUTH)
                        CyInterface().addMessage(pCaster.getOwner(),True,25,CyTranslator().getText("TXT_KEY_MESSAGE_WOLF_RIDER", ()),'',1,'Art/Interface/Buttons/Units/Wolf Rider.dds',ColorTypes(8), newUnit.getX(), newUnit.getY(),True,True)
		newUnit.convert(pCaster)

Finishmoves() could be a consideration. Just saying. Poor wolfie gets a beat down, has to carry a fat smelly goblin and still has 3 movement points? Hey, I understand, they are tough.

EDIT: Btw, I don't think the second line below (and similar) is really necessary but I could be wrong (some elif could be used at that section of event manager as well):
Code:
if pPlayer.getStateReligion() == gc.getInfoTypeForString('RELIGION_THE_ASHEN_VEIL'):
			if pPlayer.getCurrentEra() != gc.getInfoTypeForString('ERA_VEIL'):
				pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_VEIL'))

Removing that useless check at endplayerturn is also ok.

Using elif at onBuildingBuilt could be better instead of checking always all the buildings mentioned (similar for unitbuilt, etc.)

Code:
if gc.getGame().getBuildingClassCreatedCount(gc.getInfoTypeForString("BUILDINGCLASS_SOUL_FORGE")) > 0:
should be used at the start of the code for the soul forge onunitkilled. There's no reason for the loop and those checks every time a unit is killed if the building was not created. Too many games that thing is never built.

Code:
for i in xrange(21):
	pPlot2 = pCity.getCityIndexPlot(i)

can probably be used onCityDoTurn instead of
Code:
for iiX in range(iX-2, iX+3, 1):
		for iiY in range(iY-2, iY+3, 1):
 
It appears that setCurrentEra(iEra) checks to make sure that the old and new era are not the same before it does much anyway, so those checks do seem unnecessary.

Is there a need to check or change eras every turn anyway? Couldn't it be handled in def onPlayerChangeStateReligion(self, argsList): ?
 
In honor of my favorite unit, postCombatWolfRider should be like this:

OK. I'll also consider adding a finishMoves() call.

EDIT: Btw, I don't think the second line below (and similar) is really necessary but I could be wrong (some elif could be used at that section of event manager as well):

Right on both counts. Will do.

Removing that useless check at endplayerturn is also ok.

The change civic popup?

Using elif at onBuildingBuilt could be better instead of checking always all the buildings mentioned (similar for unitbuilt, etc.)

Sounds good. Added elif at a number of places.

Code:
if gc.getGame().getBuildingClassCreatedCount(gc.getInfoTypeForString("BUILDINGCLASS_SOUL_FORGE")) > 0:
should be used at the start of the code for the soul forge onunitkilled.

Also good.

Code:
for i in xrange(21):
	pPlot2 = pCity.getCityIndexPlot(i)

can probably be used onCityDoTurn instead of
Code:
for iiX in range(iX-2, iX+3, 1):
		for iiY in range(iY-2, iY+3, 1):

Not this though. pCity isn't defined yet. The code there is looking for nearby cities.
 
Is there a need to check or change eras every turn anyway? Couldn't it be handled in def onPlayerChangeStateReligion(self, argsList): ?

Seems reasonable. Seems like we can just do a setCurrentEra() there directly without doing a check for each religion. Anyone want to write that bit of code for me?
 
The change civic popup?

Yes, well, why should that first line be checked for each player every turn? If this was not a slow mod in the late game, this was just being picky, but everything counts. Up to you, though.

Not this though. pCity isn't defined yet. The code there is looking for nearby cities.

What do you mean? Isn't this correct?:

Code:
if pCity.getNumRealBuilding(gc.getInfoTypeForString('BUILDING_CITADEL_OF_LIGHT')) > 0:
		eTeam = gc.getTeam(pPlayer.getTeam())
		iBestValue = 0
		pBestPlot = -1
		for i in xrange(21):
	                        pPlot2 = pCity.getCityIndexPlot(i)
				bEnemy = false
				bNeutral = false
				iValue = 0
				if pPlot2.isVisibleEnemyUnit(iPlayer):
                                                etc
 
Seems reasonable. Seems like we can just do a setCurrentEra() there directly without doing a check for each religion. Anyone want to write that bit of code for me?

Code:
	def onPlayerChangeStateReligion(self, argsList):
		'Player changes his state religion'
		iPlayer, iNewReligion, iOldReligion = argsList
		pPlayer = gc.getPlayer(iPlayer)

		if iNewReligion == gc.getInfoTypeForString('RELIGION_RUNES_OF_KILMORPH'):
			pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_RUNE'))

		elif iNewReligion == gc.getInfoTypeForString('RELIGION_FELLOWSHIP_OF_LEAVES'):
			pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_LEAF'))

		elif iNewReligion == gc.getInfoTypeForString('RELIGION_OCTOPUS_OVERLORDS'):
			pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_OCTO'))

		elif iNewReligion == gc.getInfoTypeForString('RELIGION_THE_EMPYREAN'):
			pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_EMPY'))

		elif iNewReligion == gc.getInfoTypeForString('RELIGION_COUNCIL_OF_ESUS'):
			pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_COUN'))

		elif iNewReligion == gc.getInfoTypeForString('RELIGION_THE_ASHEN_VEIL'):
			pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_VEIL'))

		elif iNewReligion == gc.getInfoTypeForString('RELIGION_THE_ORDER'):
			pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_ORDE'))
 
The above suggested code would leave the player with religious art styles if it goes to no state religion.

I'd think he's want something more like this:
Code:
	def onPlayerChangeStateReligion(self, argsList):
		'Player changes his state religion'
		iPlayer, iNewReligion, iOldReligion = argsList

		if iNewReligion != iOldReligion:

			if iNewReligion == -1:
				iCurrentEra = pPlayer.getCurrentEra()
				iEra = iCurrentEra
				if not (iCurrentEra == gc.getInfoTypeForString('ERA_ANCIENT') or iCurrentEra == gc.getInfoTypeForString('ERA_CLASSICAL') or iCurrentEra == gc.getInfoTypeForString('ERA_MEDIEVAL')):
					iEra = gc.getGame().getStartEra()
					pPlayer.setCurrentEra(iEra)
			elif iNewReligion == gc.getInfoTypeForString('RELIGION_THE_ORDER'):
				pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_ORDE'))
			elif iNewReligion == gc.getInfoTypeForString('RELIGION_THE_EMPYREAN'):
				pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_EMPY'))
			elif iNewReligion == gc.getInfoTypeForString('RELIGION_RUNES_OF_KILMORPH'):
				pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_RUNE'))
			elif iNewReligion == gc.getInfoTypeForString('RELIGION_FELLOWSHIP_OF_LEAVES'):
				pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_LEAF'))
			elif iNewReligion == gc.getInfoTypeForString('RELIGION_OCTOPUS_OVERLORDS'):
				pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_OCTO'))
			elif iNewReligion == gc.getInfoTypeForString('RELIGION_COUNCIL_OF_ESUS'):
				pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_COUN'))
			elif iNewReligion == gc.getInfoTypeForString('RELIGION_THE_ASHEN_VEIL'):
				pPlayer.setCurrentEra(gc.getInfoTypeForString('ERA_VEIL'))

The thought comes to mind that the code might have been done per turn because it is possible for a player that a player could convert to a religion early and then loose the custom artwork when he researches technologies that make him advance to the classical or medieval eras.

To get around this, there might also hve to be some code added to def onTechAcquired(self, argsList):

Edit: I just played a game with the Ashen Veil state religion going from having no techs to having all of them, and never saw my era art stye change; I guess the tech issue is not a big deal.
 
Yep, forgot no state. Very nice. Well, I actually just assumed there must be a reason if Kael put the codes where they are but I'm stll suprised at it.

Btw, and just curiosity, do the reqs for the world spells are checked for every unit every turn?
 
When using food to production civics/producing settlers or workers, the city plot AI doesn't take into account that production multipliers do not influence food-to-production hammers. This can cause some very sub-optimal tile decisions by the automated AI in these situations. This has probably existed since vanilla I'm sure.

Easy to reproduce: get heroic epic and conquest in a city with some farms and mines. Watch as AI chooses farms over mines for production.
 
You can use that religion code only if there are no votes which will force players to change religions, aka free religion civic in bts.

Forced civics will not trigger those codes even though religion is changed.
 
The Taunt spell seems to cause a crash whenever its target units are in a group.

I think the best way to get around this would be to cause any unit that does not resist the spell to become separated from its group before it attacks.

In order to do this, you would need to expose the python code that causes a unit to leave or join a group. I'd really like you to do that anyway, so I could modify group membership in worldbuilder.
 
The Taunt spell seems to cause a crash whenever its target units are in a group.

OK. I'll look into it.

In order to do this, you would need to expose the python code that causes a unit to leave or join a group. I'd really like you to do that anyway, so I could modify group membership in worldbuilder.

It's already exposed -- void CyUnit::joinGroup(CySelectionGroup* pNewGroup)

Use joinGroup(NULL) to force a unit to leave its group.
 
I don't think I can use NULL in python.

When I use pUnit.joinGroup(NULL) I get this:

Code:
Traceback (most recent call last):

  File "CvSpellInterface", line 49, in cast

  File "<string>", line 0, in ?

  File "CvSpellInterface", line 8011, in spellTaunt

NameError: global name 'NULL' is not defined
ERR: Python function cast failed, module CvSpellInterface

When I use pUnit.joinGroup(-1) I get this:
Code:
Traceback (most recent call last):

  File "CvSpellInterface", line 49, in cast

  File "<string>", line 0, in ?

  File "CvSpellInterface", line 8011, in spellTaunt

ArgumentError: Python argument types in
    CyUnit.joinGroup(CyUnit, int)
did not match C++ signature:
    joinGroup(class CyUnit {lvalue}, class CySelectionGroup *)
ERR: Python function cast failed, module CvSpellInterface
 
"None" doesn't work either.

pUnit.joinGroup(gc.getPlayer(pUnit.getOwner()).getSelectionGroup(-1)) does seem to work for splitting up the groups.

However, I'm thinking that the group may not have been the issue. I seem to be getting crashes now when it works to force multiple units (from different groups and even tiles) to attack too.

I thought the issue could be that an attacking unit might leave the stack and change the index of the other units, but it didn't help when I changed the code to add applicable units to a list and then cycle through that after the cycling through the adjacent plots was done.

Edit: I've come across the occasional crash even when only one unit is forced to attack, although is relatively rare.

The crash is not causing anything to show up in PythonErr.log, but when I check BBAI.log I find that the last thing to occur always seems to be the unit that cast Taunt being slain in combat with one of the units that the spell caused to attack him.

I'm fairly confident now that the crash to desktop is related to the caster being killed before the spell's code has fully run its course.
 
Edit: I've come across the occasional crash even when only one unit is forced to attack, although is relatively rare.

The crash is not causing anything to show up in PythonErr.log, but when I check BBAI.log I find that the last thing to occur always seems to be the unit that cast Taunt being slain in combat with one of the units that the spell caused to attack him.

I'm fairly confident now that the crash to desktop is related to the caster being killed before the spell's code has fully run its course.

It's related to that damned doDelayedDeath() function. This thing has caused me a lot of heartache.
 
In a game as the Balseraphs where I'm able to steal units with loyalty with Mind III's domination. They should die according to the tooltip for loyalty. This is with 2.52 but I didn't see a change with 2.53.
 
Back
Top Bottom