I've made some progress, but it'll still take a while. There are no major bugfixes compared to the last version.Any idea when the next MNAI update may be out?
I've made some progress, but it'll still take a while. There are no major bugfixes compared to the last version.Any idea when the next MNAI update may be out?
Sure, noted.Can you expose pUnit.setDelayedSpell(iSpell) to python?
I expect to release relatively soon, but I need to do some more playtesting. Probably no later than the end of the month.Any ETA on the next MNAI release? I was hoping to have the next MagisterModmod update (nothing major, mostly big fixes) out over a month ago, but have been delayed by household repairs. I just got back into it and may not have much time for play testing this month we have a work deadline approaching on the 22nd. It would of course be nice to be able to include a DLL that reduces the risks of crashes.
The resource icon shrinkage is the main one to me and I think it would be to other people too (saw the recent discussion that some people turn the display off presumably because the default size is obnoxious). Other things to consider: abilty to change what happens when a settler is selected, ability to toggle notifications for jungle/forest growth outside one's borders (I'd also suggest something specific for FfH: notifications for terraforming ocurring (maybe lair exploration too). I play with citizen automation always off and it's tedious when the illians are in the game to scroll over the map every turn to make sure new tundra/ice isn't being worked), toggle for autosave notifcations, toggle for contact greetings, ability to right-click to exit the city screen (don't remember if this is already in mnai), some edits to make the "Actual effects" displays more logical, and some bugfixes to ple (which in mnai is partially broken, such as the health/movement bars).Do you have any requests?
This is not something that has to wait until the next release. It can be modded easily using only python.If it is possible, I would like to request a small game option: A setting that will not prevent hell terrain (like the option that we have right now), but will cause the plot counter of terrain to slowly decrease again if the Infernals have been destroyed. It feels thematic to have the Infernals accompanied by hell terrain, but once hell terrain gets below mountains or the ocean, it's basically impossible to get rid of ever again, which causes annoying micromanagement. It would be nice to have the banishment of the infernals rewarded by a) stopping of hell terrain spread and b) hell terrain slowly disappearing on its own again.
Could make an exception for Ashen Veil aligned territory, where it can still persist.
Hope you see the appeal of my idea
def doHellTerrain(self):
iAshenVeil = gc.getInfoTypeForString('RELIGION_THE_ASHEN_VEIL')
iBurningSands = gc.getInfoTypeForString('TERRAIN_BURNING_SANDS')
iBanana = gc.getInfoTypeForString('BONUS_BANANA')
iCotton = gc.getInfoTypeForString('BONUS_COTTON')
iCorn = gc.getInfoTypeForString('BONUS_CORN')
iCow = gc.getInfoTypeForString('BONUS_COW')
iEvil = gc.getInfoTypeForString('ALIGNMENT_EVIL')
iFarm = gc.getInfoTypeForString('IMPROVEMENT_FARM')
iFlames = gc.getInfoTypeForString('FEATURE_FLAMES')
iFlamesSpreadChance = gc.getDefineINT('FLAMES_SPREAD_CHANCE')
iGulagarm = gc.getInfoTypeForString('BONUS_GULAGARM')
iHorse = gc.getInfoTypeForString('BONUS_HORSE')
iInfernal = gc.getInfoTypeForString('CIVILIZATION_INFERNAL')
iMarble = gc.getInfoTypeForString('BONUS_MARBLE')
iNeutral = gc.getInfoTypeForString('ALIGNMENT_NEUTRAL')
iNightmare = gc.getInfoTypeForString('BONUS_NIGHTMARE')
iPig = gc.getInfoTypeForString('BONUS_PIG')
iRazorweed = gc.getInfoTypeForString('BONUS_RAZORWEED')
iRice = gc.getInfoTypeForString('BONUS_RICE')
iSheep = gc.getInfoTypeForString('BONUS_SHEEP')
iSheutStone = gc.getInfoTypeForString('BONUS_SHEUT_STONE')
iSilk = gc.getInfoTypeForString('BONUS_SILK')
iSnakePillar = gc.getInfoTypeForString('IMPROVEMENT_SNAKE_PILLAR')
iSugar = gc.getInfoTypeForString('BONUS_SUGAR')
iToad = gc.getInfoTypeForString('BONUS_TOAD')
iWheat = gc.getInfoTypeForString('BONUS_WHEAT')
iForest = gc.getInfoTypeForString('FEATURE_FOREST')
iJungle = gc.getInfoTypeForString('FEATURE_JUNGLE')
iAForest = gc.getInfoTypeForString('FEATURE_FOREST_ANCIENT')
iNForest = gc.getInfoTypeForString('FEATURE_FOREST_NEW')
iBForest = gc.getInfoTypeForString('FEATURE_FOREST_BURNT')
iCount = CyGame().getGlobalCounter()
#Alekseyev_'s game option start
bInfernalsVanquished = False
if gc.getGame().isOption(gc.getInfoTypeForString('GAMEOPTION_DUMMY_01')):
iPlayerInfernal = self.getCivilization(iInfernal)
if iPlayerInfernal != -1:
pPlayerInfernal = gc.getPlayer(iPlayerInfernal)
bInfernalsVanquished = not pPlayerInfernal.isAlive()
#Alekseyev_'s game option stop
for i in range (CyMap().numPlots()):
pPlot = CyMap().plotByIndex(i)
iFeature = pPlot.getFeatureType()
iTerrain = pPlot.getTerrainType()
iBonus = pPlot.getBonusType(-1)
iImprovement = pPlot.getImprovementType()
bUntouched = True
if pPlot.isOwned():
pPlayer = gc.getPlayer(pPlot.getOwner())
iAlignment = pPlayer.getAlignment()
if pPlayer.getCivilizationType() == iInfernal:
pPlot.changePlotCounter(100)
bUntouched = False
if (bUntouched and pPlayer.getStateReligion() == iAshenVeil or (iCount >= 50 and iAlignment == iEvil) or (iCount >= 75 and iAlignment == iNeutral)):
iX = pPlot.getX()
iY = pPlot.getY()
for iiX in range(iX-1, iX+2, 1):
for iiY in range(iY-1, iY+2, 1):
pAdjacentPlot = CyMap().plot(iiX,iiY)
if pAdjacentPlot.isNone() == False:
if pAdjacentPlot.getPlotCounter() > 10:
pPlot.changePlotCounter(1)
bUntouched = False
if (bUntouched and pPlot.isOwned() == False and iCount > 25):
iX = pPlot.getX()
iY = pPlot.getY()
for iiX in range(iX-1, iX+2, 1):
for iiY in range(iY-1, iY+2, 1):
pAdjacentPlot = CyMap().plot(iiX,iiY)
if pAdjacentPlot.isNone() == False:
if pAdjacentPlot.getPlotCounter() > 10:
pPlot.changePlotCounter(1)
bUntouched = False
iPlotCount = pPlot.getPlotCounter()
if (bUntouched and iPlotCount > 0):
pPlot.changePlotCounter(-1)
if bInfernalsVanquished:#Alekseyev_'s game option
pPlot.changePlotCounter(-1)#Alekseyev_'s game option
if iPlotCount > 9:
if (iBonus == iSheep or iBonus == iPig):
pPlot.setBonusType(iToad)
if (iBonus == iHorse or iBonus == iCow):
pPlot.setBonusType(iNightmare)
if (iBonus == iCotton or iBonus == iSilk):
pPlot.setBonusType(iRazorweed)
if (iBonus == iBanana or iBonus == iSugar):
pPlot.setBonusType(iGulagarm)
if (iBonus == iMarble):
pPlot.setBonusType(iSheutStone)
if (iBonus == iCorn or iBonus == iRice or iBonus == iWheat):
pPlot.setBonusType(-1)
pPlot.setImprovementType(iSnakePillar)
if (iFeature == iForest or iFeature == iAForest or iFeature == iNForest or iFeature == iJungle):
iRandom = CyGame().getSorenRandNum(100, "Hell Terrain Burnt Forest")
if iRandom < 10:
pPlot.setFeatureType(iBForest, 0)
if pPlot.isPeak() == True:
iRandom = CyGame().getSorenRandNum(1000, "Hell Terrain Volcanos")
if iRandom < 2:
iEvent = CvUtil.findInfoTypeNum(gc.getEventTriggerInfo, gc.getNumEventTriggerInfos(), 'EVENTTRIGGER_VOLCANO_CREATION')
if pPlot.isOwned():
triggerData = pPlayer.initTriggeredData(iEvent, True, -1, pPlot.getX(), pPlot.getY(), -1, -1, -1, -1, -1, -1)
if iPlotCount < 10:
if iBonus == iToad:
if CyGame().getSorenRandNum(100, "Hell Convert") < 50:
pPlot.setBonusType(iSheep)
else:
pPlot.setBonusType(iPig)
if iBonus == iNightmare:
if CyGame().getSorenRandNum(100, "Hell Convert") < 50:
pPlot.setBonusType(iHorse)
else:
pPlot.setBonusType(iCow)
if iBonus == iRazorweed:
if CyGame().getSorenRandNum(100, "Hell Convert") < 50:
pPlot.setBonusType(iCotton)
else:
pPlot.setBonusType(iSilk)
if iBonus == iGulagarm:
if CyGame().getSorenRandNum(100, "Hell Convert") < 50:
pPlot.setBonusType(iBanana)
else:
pPlot.setBonusType(iSugar)
if (iBonus == iSheutStone):
pPlot.setBonusType(iMarble)
if iImprovement == iSnakePillar:
pPlot.setImprovementType(iFarm)
iCount = CyGame().getSorenRandNum(100, "Hell Convert")
if iCount < 33:
pPlot.setBonusType(iCorn)
else:
if iCount < 66:
pPlot.setBonusType(iRice)
else:
pPlot.setBonusType(iWheat)
if iTerrain == iBurningSands:
if pPlot.isCity() == False:
if pPlot.isPeak() == False:
if CyGame().getSorenRandNum(100, "Flames") < iFlamesSpreadChance:
pPlot.setFeatureType(iFlames, 0)
def doHellTerrain(self):
iAshenVeil = gc.getInfoTypeForString('RELIGION_THE_ASHEN_VEIL')
iBurningSands = gc.getInfoTypeForString('TERRAIN_BURNING_SANDS')
iBanana = gc.getInfoTypeForString('BONUS_BANANA')
iCotton = gc.getInfoTypeForString('BONUS_COTTON')
iCorn = gc.getInfoTypeForString('BONUS_CORN')
iCow = gc.getInfoTypeForString('BONUS_COW')
iEvil = gc.getInfoTypeForString('ALIGNMENT_EVIL')
iFarm = gc.getInfoTypeForString('IMPROVEMENT_FARM')
iFlames = gc.getInfoTypeForString('FEATURE_FLAMES')
iFlamesSpreadChance = gc.getDefineINT('FLAMES_SPREAD_CHANCE')
iGulagarm = gc.getInfoTypeForString('BONUS_GULAGARM')
iHorse = gc.getInfoTypeForString('BONUS_HORSE')
iInfernal = gc.getInfoTypeForString('CIVILIZATION_INFERNAL')
iMarble = gc.getInfoTypeForString('BONUS_MARBLE')
iNeutral = gc.getInfoTypeForString('ALIGNMENT_NEUTRAL')
iNightmare = gc.getInfoTypeForString('BONUS_NIGHTMARE')
iPig = gc.getInfoTypeForString('BONUS_PIG')
iRazorweed = gc.getInfoTypeForString('BONUS_RAZORWEED')
iRice = gc.getInfoTypeForString('BONUS_RICE')
iSheep = gc.getInfoTypeForString('BONUS_SHEEP')
iSheutStone = gc.getInfoTypeForString('BONUS_SHEUT_STONE')
iSilk = gc.getInfoTypeForString('BONUS_SILK')
iSnakePillar = gc.getInfoTypeForString('IMPROVEMENT_SNAKE_PILLAR')
iSugar = gc.getInfoTypeForString('BONUS_SUGAR')
iToad = gc.getInfoTypeForString('BONUS_TOAD')
iWheat = gc.getInfoTypeForString('BONUS_WHEAT')
iForest = gc.getInfoTypeForString('FEATURE_FOREST')
iJungle = gc.getInfoTypeForString('FEATURE_JUNGLE')
iAForest = gc.getInfoTypeForString('FEATURE_FOREST_ANCIENT')
iNForest = gc.getInfoTypeForString('FEATURE_FOREST_NEW')
iBForest = gc.getInfoTypeForString('FEATURE_FOREST_BURNT')
iCount = CyGame().getGlobalCounter()
#Alekseyev_'s game option start
bInfernalsVanquished = False
if gc.getGame().isOption(gc.getInfoTypeForString('GAMEOPTION_DUMMY_01')):#Make this extra game option cause Hell terrain to fade once the Infernals are vanquished
iNumDemonLords = 0
iNumVanquishedDemonLords = 0
for iPlayer2 in xrange(gc.getMAX_PLAYERS()):
pPlayer2 = gc.getPlayer(iPlayer2)
if pPlayer2.getCivilizationType() == iInfernal:
iNumDemonLords += 1
if not pPlayer2.isAlive():
iNumVanquishedDemonLords += 1
if iNumDemonLords > 0:
if iNumVanquishedDemonLords == iNumDemonLords:
bInfernalsVanquished = True
#Alekseyev_'s game option stop
for i in range (CyMap().numPlots()):
pPlot = CyMap().plotByIndex(i)
iFeature = pPlot.getFeatureType()
iTerrain = pPlot.getTerrainType()
iBonus = pPlot.getBonusType(-1)
iImprovement = pPlot.getImprovementType()
bUntouched = True
if pPlot.isOwned():
pPlayer = gc.getPlayer(pPlot.getOwner())
iAlignment = pPlayer.getAlignment()
if pPlayer.getCivilizationType() == iInfernal:
pPlot.changePlotCounter(100)
bUntouched = False
if (bUntouched and pPlayer.getStateReligion() == iAshenVeil or (iCount >= 50 and iAlignment == iEvil) or (iCount >= 75 and iAlignment == iNeutral)):
iX = pPlot.getX()
iY = pPlot.getY()
for iiX in range(iX-1, iX+2, 1):
for iiY in range(iY-1, iY+2, 1):
pAdjacentPlot = CyMap().plot(iiX,iiY)
if pAdjacentPlot.isNone() == False:
if pAdjacentPlot.getPlotCounter() > 10:
pPlot.changePlotCounter(1)
bUntouched = False
if (bUntouched and pPlot.isOwned() == False and iCount > 25):
iX = pPlot.getX()
iY = pPlot.getY()
for iiX in range(iX-1, iX+2, 1):
for iiY in range(iY-1, iY+2, 1):
pAdjacentPlot = CyMap().plot(iiX,iiY)
if pAdjacentPlot.isNone() == False:
if pAdjacentPlot.getPlotCounter() > 10:
pPlot.changePlotCounter(1)
bUntouched = False
iPlotCount = pPlot.getPlotCounter()
if (bUntouched and iPlotCount > 0):
pPlot.changePlotCounter(-1)
if bInfernalsVanquished:#Alekseyev_'s game option
pPlot.changePlotCounter(-1)#Alekseyev_'s game option
if iPlotCount > 9:
if (iBonus == iSheep or iBonus == iPig):
pPlot.setBonusType(iToad)
if (iBonus == iHorse or iBonus == iCow):
pPlot.setBonusType(iNightmare)
if (iBonus == iCotton or iBonus == iSilk):
pPlot.setBonusType(iRazorweed)
if (iBonus == iBanana or iBonus == iSugar):
pPlot.setBonusType(iGulagarm)
if (iBonus == iMarble):
pPlot.setBonusType(iSheutStone)
if (iBonus == iCorn or iBonus == iRice or iBonus == iWheat):
pPlot.setBonusType(-1)
pPlot.setImprovementType(iSnakePillar)
if (iFeature == iForest or iFeature == iAForest or iFeature == iNForest or iFeature == iJungle):
iRandom = CyGame().getSorenRandNum(100, "Hell Terrain Burnt Forest")
if iRandom < 10:
pPlot.setFeatureType(iBForest, 0)
if pPlot.isPeak() == True:
iRandom = CyGame().getSorenRandNum(1000, "Hell Terrain Volcanos")
if iRandom < 2:
iEvent = CvUtil.findInfoTypeNum(gc.getEventTriggerInfo, gc.getNumEventTriggerInfos(), 'EVENTTRIGGER_VOLCANO_CREATION')
if pPlot.isOwned():
triggerData = pPlayer.initTriggeredData(iEvent, True, -1, pPlot.getX(), pPlot.getY(), -1, -1, -1, -1, -1, -1)
if iPlotCount < 10:
if iBonus == iToad:
if CyGame().getSorenRandNum(100, "Hell Convert") < 50:
pPlot.setBonusType(iSheep)
else:
pPlot.setBonusType(iPig)
if iBonus == iNightmare:
if CyGame().getSorenRandNum(100, "Hell Convert") < 50:
pPlot.setBonusType(iHorse)
else:
pPlot.setBonusType(iCow)
if iBonus == iRazorweed:
if CyGame().getSorenRandNum(100, "Hell Convert") < 50:
pPlot.setBonusType(iCotton)
else:
pPlot.setBonusType(iSilk)
if iBonus == iGulagarm:
if CyGame().getSorenRandNum(100, "Hell Convert") < 50:
pPlot.setBonusType(iBanana)
else:
pPlot.setBonusType(iSugar)
if (iBonus == iSheutStone):
pPlot.setBonusType(iMarble)
if iImprovement == iSnakePillar:
pPlot.setImprovementType(iFarm)
iCount = CyGame().getSorenRandNum(100, "Hell Convert")
if iCount < 33:
pPlot.setBonusType(iCorn)
else:
if iCount < 66:
pPlot.setBonusType(iRice)
else:
pPlot.setBonusType(iWheat)
if iTerrain == iBurningSands:
if pPlot.isCity() == False:
if pPlot.isPeak() == False:
if CyGame().getSorenRandNum(100, "Flames") < iFlamesSpreadChance:
pPlot.setFeatureType(iFlames, 0)
<GameOptionInfo>
<Type>GAMEOPTION_DUMMY_01</Type>
<Description>Dummy</Description>
<Help>Dummy</Help>
<bDefault>0</bDefault>
<bVisible>0</bVisible>
</GameOptionInfo>
caster.getDamage()
is 0 for undamaged units.return False
. Yeah, looks like you're right.<iAIWeight>
zero. If I increase that, the spell is used but causes an assertion. I will investigate this after the next release.Is it always causing an exception, or only if the AI unit belongs to a player who does not have a capital city?No, it uses it when the damage is 50% or more.caster.getDamage()
is 0 for undamaged units.
EDIT: Ah, I see,return False
. Yeah, looks like you're right.
EDIT2: I checked, AI currently doesn't use the spell since it has<iAIWeight>
zero. If I increase that, the spell is used but causes an assertion. I will investigate this after the next release.
<SpellInfo>
<Type>SPELL_ESCAPE</Type>
<Description>TXT_KEY_SPELL_ESCAPE</Description>
<Civilopedia>TXT_KEY_SPELL_ESCAPE_PEDIA</Civilopedia>
<Help>TXT_KEY_SPELL_ESCAPE_HELP</Help>
<UnitPrereq>UNIT_CHANTER</UnitPrereq>
<bAllowAI>1</bAllowAI>
<bDisplayWhenDisabled>1</bDisplayWhenDisabled>
<bHasCasted>1</bHasCasted>
<PyResult>spellTeleport(pCaster,'Capital')</PyResult>
<PyRequirement>reqEscape(pCaster)</PyRequirement>
<Effect>EFFECT_SPELL2</Effect>
<Sound>AS3D_SPELL_ESCAPE</Sound>
<Button>Art/Interface/Buttons/Spells/Escape.dds</Button>
</SpellInfo>
def reqEscape(caster):
if caster.getOwner() == gc.getBARBARIAN_PLAYER():
return False
pPlayer = gc.getPlayer(caster.getOwner())
if pPlayer.isHuman() == False:
if caster.getDamage() >= 50:
return False
return True
def spellTeleport(caster,loc):
player = caster.getOwner()
pPlayer = gc.getPlayer(player)
pCity = pPlayer.getCapitalCity()
caster.setXY(pCity.getX(), pCity.getY(), False, True, True)
<SpellInfo>
<Type>SPELL_ESCAPE_GREATER</Type>
<Description>TXT_KEY_SPELL_ESCAPE</Description>
<Civilopedia>TXT_KEY_SPELL_ESCAPE_PEDIA</Civilopedia>
<Help>TXT_KEY_SPELL_ESCAPE_HELP</Help>
<PromotionPrereq1>PROMOTION_AFFINITY_DIMENSIONAL</PromotionPrereq1>
<bAllowAI>1</bAllowAI>
<bDisplayWhenDisabled>1</bDisplayWhenDisabled>
<bIgnoreHasCasted>0</bIgnoreHasCasted>
<PyResult>spellEscape(pCaster, eSpell)</PyResult>
<PyRequirement>reqEscape(pCaster, eSpell)</PyRequirement>
<PyHelp>helpEscape(lpUnits, eSpell)</PyHelp>
<Effect>EFFECT_SPELL2</Effect>
<Sound>AS3D_SPELL_ESCAPE</Sound>
<Button>Art/Interface/Buttons/Spells/Escape.dds</Button>
</SpellInfo>
<SpellInfo>
<Type>SPELL_ESCAPE_FACILITATED</Type>
<Description>TXT_KEY_SPELL_ESCAPE_FACILITATED</Description>
<Civilopedia>TXT_KEY_SPELL_ESCAPE_PEDIA</Civilopedia>
<Help>TXT_KEY_SPELL_ESCAPE_HELP</Help>
<PromotionInStackPrereq>PROMOTION_AFFINITY_DIMENSIONAL</PromotionInStackPrereq>
<bAllowAI>1</bAllowAI>
<bDisplayWhenDisabled>0</bDisplayWhenDisabled>
<bHasCasted>1</bHasCasted>
<iMiscastChance>35</iMiscastChance>
<PyMiscast>miscastEscape(pCaster, eSpell)</PyMiscast>
<PyResult>spellEscape(pCaster, eSpell)</PyResult>
<PyRequirement>reqEscapeFacilitated(pCaster, eSpell)</PyRequirement>
<PyHelp>helpEscape(lpUnits, eSpell)</PyHelp>
<Effect>EFFECT_SPELL2</Effect>
<Sound>AS3D_SPELL_ESCAPE</Sound>
<Button>Art/Interface/Buttons/Spells/EscapeFacilitated.dds</Button>
</SpellInfo>
<SpellInfo>
<Type>SPELL_ESCAPE</Type>
<Description>TXT_KEY_SPELL_ESCAPE_LESSER</Description>
<Civilopedia>TXT_KEY_SPELL_ESCAPE_PEDIA</Civilopedia>
<Help>TXT_KEY_SPELL_ESCAPE_HELP</Help>
<PromotionPrereq1>PROMOTION_DIMENSIONAL1</PromotionPrereq1>
<bAllowAI>1</bAllowAI>
<bDisplayWhenDisabled>1</bDisplayWhenDisabled>
<bHasCasted>1</bHasCasted>
<iMiscastChance>25</iMiscastChance>
<PyMiscast>miscastEscape(pCaster, eSpell)</PyMiscast>
<PyResult>spellEscape(pCaster, eSpell)</PyResult>
<PyRequirement>reqEscape(pCaster, eSpell)</PyRequirement>
<PyHelp>helpEscape(lpUnits, eSpell)</PyHelp>
<Effect>EFFECT_SPELL2</Effect>
<Sound>AS3D_SPELL_ESCAPE</Sound>
<Button>Art/Interface/Buttons/Spells/EscapeLesser.dds</Button>
</SpellInfo>
def reqEscape(pCaster, eSpell=-1):
if not reqDimensionalAllowed(pCaster):
return False
iPlayer = pCaster.getOwner()
pPlayer = gc.getPlayer(iPlayer)
if not pPlayer.isHuman():
if pCaster.getDamage() < 50:
return False
pPlot = -1
if pCaster.getSummoner() != -1:
pSummoner = pPlayer.getUnit(pCaster.getSummoner())
if not pSummoner.isNone():
if not pCaster.atPlot(pSummoner.plot()):
pPlot = pSummoner.plot()
if pPlot == -1:
pCapital = pPlayer.getCapitalCity()
if pCapital.isNone():
return False
if pCapital.atPlot(pCaster.plot()):
return False
if not pCapital.isCoastal(1):
if pCaster.getDomainType() == gc.getInfoTypeForString('DOMAIN_SEA'):
return False
return True
def reqEscapeFacilitated(pCaster, eSpell=1):
if pCaster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_DIMENSIONAL1')):
return False
## if pCaster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_AFFINITY_DIMENSIONAL')):#I believe <PromotionInStackPrereq> already eliminates these units
## return False
return reqEscape(pCaster, eSpell)
def miscastEscape(pCaster, eSpell):
pPlot = pCaster.plot()
bWater = pCaster.getDomainType() == gc.getInfoTypeForString('DOMAIN_SEA')
iBestValue = 0
pBestPlot = -1
for i in xrange (CyMap().numPlots()):
iValue = 0
pTargetPlot = CyMap().plotByIndex(i)
if bWater == pTargetPlot.isWater():
iValue = CyGame().getSorenRandNum(1000, "Escape miscast move "+ str(pCaster.getName()))
if not pTargetPlot.isOwned():
iValue += 1000
if pTargetPlot == pPlot:
iValue = 0
if pTargetPlot.isCity():
iValue = 0
if iValue > iBestValue:
iBestValue = iValue
pBestPlot = pTargetPlot
if pBestPlot != -1:
pCaster.setXY(pBestPlot.getX(), pBestPlot.getY(), False, True, True)
def spellEscape(pCaster, eSpell=-1):
iPlayer = pCaster.getOwner()
pPlayer = gc.getPlayer(iPlayer)
pPlot = -1
pCapital = pPlayer.getCapitalCity()
if not pCapital.isNone():
if pCapital.isCoastal(1) or pCaster.getDomainType() != gc.getInfoTypeForString('DOMAIN_SEA'):
pPlot = pCapital.plot()
if pPlot == -1:
if pCaster.getSummoner() != -1:
pSummoner = pPlayer.getUnit(pCaster.getSummoner())
if not pSummoner.isNone():
if not pCaster.atPlot(pSummoner.plot()):
pPlot = pSummoner.plot()
if pPlot != -1:
pCaster.setXY(pPlot.getX(), pPlot.getY(), False, True, True)
def helpEscape(lpUnits, eSpell=1):
szBuffer = ''
pCaster = lpUnits[0]
iPlayer = pCaster.getOwner()
pPlayer = gc.getPlayer(iPlayer)
sNameRefuge = ''
for pCaster in lpUnits:
iX1 = pCaster.getX()
iY1 = pCaster.getY()
pExitPlot = -1
iX2 = -1
iY2 = -1
pCapital = pPlayer.getCapitalCity()
if not pCapital.isNone():
if pCapital.isCoastal(1) or pCaster.getDomainType() != gc.getInfoTypeForString('DOMAIN_SEA'):
if not pCaster.atPlot(pCapital.plot()):
pExitPlot = pCapital.plot()
iX2 = pCapital.getX()
iY2 = pCapital.getY()
sNameRefuge ="<color=%d,%d,%d,%d>%s</color>" %(pPlayer.getPlayerTextColorR(), pPlayer.getPlayerTextColorG(), pPlayer.getPlayerTextColorB(), pPlayer.getPlayerTextColorA(), pCapital.getName())
if pExitPlot == -1:
if pCaster.getSummoner() != -1:
pSummoner = pPlayer.getUnit(pCaster.getSummoner())
if not pSummoner.isNone():
if not pCaster.atPlot(pSummoner.plot()):
pExitPlot = pSummoner.plot()
iX2 = pSummoner.getX()
iY2 = pSummoner.getY()
sNameRefuge = cf.getNameWithColorScheme(pSummoner, pSummoner.isInvisible(pCaster.getTeam(), False))
if not (pExitPlot == -1 or pExitPlot.isNone()):
sList = cf.getNameWithColorScheme(pCaster)
szBuffer += localText.getText("TXT_KEY_SPELL_ESCAPE", ()) + ' to ' + sNameRefuge
idX = iX2 - iX1
if idX != 0:
sdX = 'East'
if idX < 0:
idX = -idX
sdX = 'West'
szBuffer += localText.getText("TXT_KEY_HELP_MOVE_UNIT", (sList,idX, sdX, ))
idY = iY2 - iY1
if idY != 0:
sdY = 'North'
if idY < 0:
idY = -idY
sdY = 'South'
szBuffer += localText.getText("TXT_KEY_HELP_MOVE_UNIT", (sList,idY, sdY, ))
szBuffer += helpManaBlocked(lpUnits, 'VOTE_NO_DIMENSIONAL_MANA')
return szBuffer
Puppet states get capitals, but Rebels may not. I cannot recall if I've ever seen Adepts among the units generated by rebellions before they capture a city though. (Based on the lore, a Dimensional mage leading a rebellion makes almost as much sense as Fire/Spirit mages like Soren Castamer instigating the revolt.)You're right that it fails if the player has no capital city. (Is this currently possible, though? I think puppet states currently do get capitals.)
But that wasn't the problem, I got an assertion (in the DLL) complaining about the unit's coordinates not being right (and then an assertion about an infinite AI loop). The Wildmana AI code Tholal merged lets the AI cast spells at a weird time in the update cycle. There's probably an implicit assumption somewhere that the unit doesn't change location when casting the spell.
Right, good point.Puppet states get capitals, but Rebels may not.
Can you share the code for your spell staffs?That reminded me of a minor issue I've come across in my modmod, where I have Spellstaffs passively allow 2 spells per turn. A lot of spells disappear from the options a unit has after being cast once, only reappearing if I select another unit and then go back to that caster. Also, it can be annoying when casting one spell causes a unit to be deselected when it should be able to cast a second spell immediately.