[MOD] MagisterModmod

I had a mage (serving at the town wizard) sitting in a town with the Infernal Grimoire and he suddenly vanished. I know they can get killed when reading it, but he was under strict instructions to not open that book. Will they do that on their own sometimes or maybe the book just gobbled them up. Its nowhere to be found either.

Thanks for all of your work on this all of the little twisty bits you've added make it tons of fun.

*I found them... they were in some other city, i must have accidentally moved them there* Then I discovered a "waiting for other civilizations" loop. :( Dang. This was a neat game.
 
Last edited:
Then I discovered a "waiting for other civilizations" loop. :( Dang. This was a neat game.

Unfortunately, this modmod has lots of content, which is great, but bugs man... it's unplayable. Have a nice game for a 200 turns then it begins. I wonder if anyone has finished a game without a crash or AI loop.
 
The second Auric thing isn't a bug, the tile that he is standing on gives entropy mana, the side bar does not matter for him. And yes, the draw causes everyone who is not teamed or vassaled with auric to dec war on you, this is a big AI tax as the AI starts casting spells everywhere. You could stop a bunch of the casting lag if you could somehow make the interturn go full fog so the game doesn't load the spell effects and sound off screen from tiles you have vision on but are not focused.
False. He gets the specific mana spell on every mana tile he stands on. If you move him, he loses it.
 
Angel of Death or Runewyn appear from the Tapestry House, when opponent explores the Tapestry House between my turns, they attack my opponent and the game crashes.
----------
Ring of Carcer does not spawn on map when using All Unique Features option.
----------
I can't load the birds onto the Man O'War.
 
Last edited:
Angel of Death or Runewyn appear from the Tapestry House, when opponent explores the Tapestry House between my turns, they attack my opponent and the game crashes.
I am fixed by replacing pPlot with pPlot2
Spoiler CvSpellInterface.py :

def miscastSummonBarbarian(pCaster, eSpell, iNum = 1):
pPlot = pCaster.plot()
if eSpell != -1:
iUnit = gc.getSpellInfo(eSpell).getCreateUnitType()
iNum = gc.getSpellInfo(eSpell).getCreateUnitNum()
if pCaster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_TWINCAST')):
iNum *= 2
if iUnit > -1:
pPlayer = gc.getPlayer(gc.getBARBARIAN_PLAYER())
for i in range(iNum):
pPlot2 = findClearPlot(-1, pPlot)
if pPlot2 != -1:
newUnit = pPlayer.initUnit(iUnit, pPlot2.getX(), pPlot2.getY(), UnitAITypes.UNITAI_ATTACK_CITY, DirectionTypes.DIRECTION_SOUTH)
newUnit.setDuration(2)
if not gc.getTeam(gc.getBARBARIAN_TEAM()).isAtWar(pCaster.getTeam()):
newUnit.setHasPromotion(gc.getInfoTypeForString('PROMOTION_PARANOID'), True)
for iProm in cf.getSummonPerks(pCaster):
newUnit.setHasPromotion(iProm, True)
if not newUnit.isOnlyDefensive():
newUnit.setHasPromotion(gc.getInfoTypeForString('PROMOTION_ENRAGED'), True)
if newUnit.canMoveOrAttackInto(pPlot2, False):
newUnit.attack(pPlot2, False)
 
hey! great mod, but im running into an issue - as Lanun, my pirate coves are not upgrading at all. The upgrade timer runs down, and on the turn they're supposed to upgrade, it gives the "needs to be worked" note and then on the next turn the upgrade timer starts over. I have checked - the city is working those tiles, I have warriors and boats stationed on those tiles, and I can't even place upgraded harbors/ports in the worldbuilder - it just places a cove instead.
Whether upgrading is based on a unit being fortified there or based on being worked depends on whether the superfort game option is active.

I'd forgotten when I first read this post, but a while back I added this code in CvEventManager.py under def onImprovementBuilt(self, argsList):
Spoiler :

Code:
                        elif iImprovement == gc.getInfoTypeForString('IMPROVEMENT_PIRATE_PORT'):
                            if iCiv not in [gc.getInfoTypeForString('CIVILIZATION_LANUN'),gc.getBARBARIAN_PLAYER()]:
                                pPlot.setOwner(gc.getBARBARIAN_PLAYER())

                            if pPlot.isOwned():
                                pPlayer = gc.getPlayer(pPlot.getOwner())
                                if not pPlayer.isHasTech(gc.getInfoTypeForString('TECH_OPTICS')):
                                    pPlot.setImprovementType( gc.getInfoTypeForString('IMPROVEMENT_PIRATE_HARBOR'))

                        elif iImprovement == gc.getInfoTypeForString('IMPROVEMENT_PIRATE_HARBOR'):
                            if iCiv not in [gc.getInfoTypeForString('CIVILIZATION_LANUN'),gc.getBARBARIAN_PLAYER()]:
                                pPlot.setOwner(gc.getBARBARIAN_PLAYER())

                            if pPlot.isOwned():
                                pPlayer = gc.getPlayer(pPlot.getOwner())
                                if not pPlayer.isHasTech(gc.getInfoTypeForString('TECH_SAILING')):
                                    pPlot.setImprovementType( gc.getInfoTypeForString('IMPROVEMENT_PIRATE_COVE'))

                        elif iImprovement == gc.getInfoTypeForString('IMPROVEMENT_PIRATE_COVE'):
                            if iCiv not in [gc.getInfoTypeForString('CIVILIZATION_LANUN'),gc.getBARBARIAN_PLAYER()]:
                                pPlot.setOwner(gc.getBARBARIAN_PLAYER())
When I left Pirate Ports and Harbors generate ships I didn't want them to start doing so before you had the techs to make such ships.

At one point I had the Coves/Harbors/Ports generate Galleys/Triremes/Pirates, but that was way too powerful so in the last release it was just Harbors/Ports generating Workboats/Pirates.

The thought just came to mind that generating Smugglers might be better.
Do you have any plan to update the "KE huge ICE Empires" map (the one with the civs already spread across Erebus, the Mercurians and Infernals summoned, etc) at some point? I'd be interested in your choice of location for the new resources and Unique Features you've introduced so that I may update the unsettled version of the map in turn. Alternatively, just the general location you'd choose for them would be interesting.

Semi-related: I've noticed that in pre-made scenarios, Dragon Bones seem to reload a random choice of mana every time a new game is started. Is there a way to stop this from happening?
I already updated it enough to prevent it from causing crashes. I don't really have any further plans for it.

Scenarios can be set up to have random resources or to use only the resources on the specific tiles where they were placed on the map.
Spoiler :

KE huge ICE Empires v1.1 for MagisterModmod.CivBeyondSwordWBSavehas
Code:
BeginMap
    grid width=128
    grid height=80
    top latitude=90
    bottom latitude=-90
    wrap X=1
    wrap Y=1
    world size=WORLDSIZE_HUGE
    climate=CLIMATE_TEMPERATE
    sealevel=SEALEVEL_MEDIUM
    num plots written=10240
    num signs written=50
    Randomize Resources=false
    Randomize Lairs=false
    Randomize Unique Improvements=false
    Randomize Goody Huts=false
EndMap
while Lord of the Balors.CivBeyondSwordWBSave has
Code:
BeginMap
    grid width=84
    grid height=52
    top latitude=85
    bottom latitude=-10
    wrap X=0
    wrap Y=0
    world size=WORLDSIZE_LARGE
    climate=CLIMATE_TEMPERATE
    sealevel=SEALEVEL_LOW
    num plots written=4368
    num signs written=0
    Randomize Resources=true
    Randomize Lairs=true
    Randomize Unique Improvements=false
    Randomize Goody Huts=true
EndMap
When Randomize Resources=false then the dragon bones should only have the bonus placed when making the scenario. When Randomize Resources=true then I think it would pick a mana type at random.


edit:

I just realized that this is not your issue.

I'd forgotten that when I made it so that Dragonbones can connect (and randomly create) any kind of mana rather than having a separate improvement for every dragon type, I added this code in CvEventManager.py under def onImprovementBuilt(self, argsList):
Spoiler :

Code:
                elif iImprovement == gc.getInfoTypeForString('IMPROVEMENT_DRAGON_BONES'):
                    iDragon = gc.getInfoTypeForString('PROMOTION_DRAGON')
                    iSluagh = gc.getInfoTypeForString('UNIT_SLUAGH')
                    for i in xrange(pPlot.getNumUnits()):
                        pUnit = pPlot.getUnit(i)
                        if pUnit.getUnitType() == iSluagh:
                            if pUnit.isHasPromotion(iDragon):
                                iUnit = pUnit.getScenarioCounter()
                                if -1 < iUnit < gc.getNumUnitInfos():
                                    infoUnit = gc.getUnitInfo(iUnit)
                                    iBonus = infoUnit.getPrereqAndBonus()
                                    pPlot.setBonusType(iBonus)
                                    break
                    else:
                        iDragon = gc.getInfoTypeForString('SPECIALUNIT_DRAGON')
                        listDragons = []
                        for iUnit in xrange(gc.getNumUnitInfos()):
                            infoUnit = gc.getUnitInfo(iUnit)
                            if infoUnit.getSpecialUnitType() == iDragon:
                                if isWorldUnitClass(infoUnit.getUnitClassType()):continue
                                listDragons.append(iUnit)
                        iDragon = listDragons[CyGame().getSorenRandNum(len(listDragons), "Dragon Bones species")]
                        newUnit = gc.getPlayer(gc.getBARBARIAN_PLAYER()).initUnit(gc.getInfoTypeForString('UNIT_SLUAGH'), iX, iY, UnitAITypes.UNITAI_HERO, DirectionTypes.DIRECTION_SOUTH)
                        newUnit.setScenarioCounter(iDragon)
                        infoUnit = gc.getUnitInfo(iDragon)
                        sUnitName =infoUnit.getDescription()
                        newUnit.setName(sUnitName + "'s Sluagh")
                        for iProm in xrange(gc.getNumPromotionInfos()):
                            if infoUnit.getFreePromotions(iProm):
                                newUnit.setHasPromotion(iProm, True)
                        iBonus = infoUnit.getPrereqAndBonus()
                        pPlot.setBonusType(iBonus)

Dragonbones should be left along with a sluagh when a dragon dies. (However, I noticed this did not happen when Acheron died in my last game. That is a bug I ought to look into.)

The first loop is checking for the sluagh and placing the appropriate mana type there. That should probably run even in scenarios in case you have a dragon die. If you want a particular type of mana, then you can place the right kind of dragon sluagh in the scenario file.

Python is unusual among coding languages in having "for -break- else" statements, where the "else" condition is called if the loop finished normally without the break condition being met. The second part of this code runs if there is no dragon sluagh there. It randomly picks a dragon type to place a dragon's sluagh there and then set the bonus type.

We could add the line if not CyGame().getWBMapScript(): to prevent part of the code form running in scenarios. Note that there is no such thing as a "for -break- elif" stateement, but you could add "if not CyGame().getWBMapScript():" within the else and thenindent the stuff below it.

It might be worth rewriting the code though. I think it may be more efficient to pick a random mana type first, or leave the mana alone if there was already mana there, and then pick a dragon based on the mana.
Mayeb something like this (which I just wrote and have not playtested yet)
Spoiler :
Code:
                elif iImprovement == gc.getInfoTypeForString('IMPROVEMENT_DRAGON_BONES'):
                    iDragon = gc.getInfoTypeForString('PROMOTION_DRAGON')
                    iSluagh = gc.getInfoTypeForString('UNIT_SLUAGH')
                    iBonus = pPlot.getBonusType(-1)
                    if iBonus == -1 or not gc.getBonusInfo(iBonus).isMana():
                        for i in xrange(pPlot.getNumUnits()):
                            pUnit = pPlot.getUnit(i)
                            if pUnit.getUnitType() == iSluagh:
                                if pUnit.isHasPromotion(iDragon):
                                    iUnit = pUnit.getScenarioCounter()
                                    if -1 < iUnit < gc.getNumUnitInfos():
                                        infoUnit = gc.getUnitInfo(iUnit)
                                        iBonus = infoUnit.getPrereqAndBonus()
                                        pPlot.setBonusType(iBonus)
                                        break
                        else:
                            if not CyGame().getWBMapScript():
                                listMana = []
                                for iLoopBonus in xrange(gc.getNumBonusInfos()):
                                    if gc.getBonusInfo(iLoopBonus).isMana():
                                        listMana.append(iLoopBonus)
                                if len(listMana) > 0:
                                    iBonus = listMana.pop(CyGame().getSorenRandNum(len(listMana), "Dragon Bones pick random mana"))
                                    pPlot.setBonusType(iBonus)
                                    iDragon = gc.getInfoTypeForString('SPECIALUNIT_DRAGON')
                                    listDragons = []
                                    for iUnit in xrange(gc.getNumUnitInfos()):
                                        infoUnit = gc.getUnitInfo(iUnit)
                                        if infoUnit.getSpecialUnitType() == iDragon:
                                            if iBonus == infoUnit.getPrereqAndBonus():
                                                if not isWorldUnitClass(infoUnit.getUnitClassType()):
                                                    listDragons.append(iUnit)
                                    if len(listDragons) > 0:
                                        iDragon = listDragons[CyGame().getSorenRandNum(len(listDragons), "Dragon Bones species")]
                                        newUnit = gc.getPlayer(gc.getBARBARIAN_PLAYER()).initUnit(gc.getInfoTypeForString('UNIT_SLUAGH'), iX, iY, UnitAITypes.UNITAI_HERO, DirectionTypes.DIRECTION_SOUTH)
                                        newUnit.setScenarioCounter(iDragon)
                                        infoUnit = gc.getUnitInfo(iDragon)
                                        sUnitName =infoUnit.getDescription()
                                        newUnit.setName(sUnitName + "'s Sluagh")
                                        for iProm in xrange(gc.getNumPromotionInfos()):
                                            if infoUnit.getFreePromotions(iProm):
                                                newUnit.setHasPromotion(iProm, True)
edit: this dragon bone code works fine. The problem with dragons not leaving bones or sluaghs seems to be caused by having if unit.isHasPromotion(iAdventurer): instead of elif unit.isHasPromotion(iAdventurer): in line 5618 of CustomFunctions.py

There's also a bug with commanders. If you build a unit and this unit has free promotions, "attaching" a commander without picking the promotions will make them vanish, making your unit vanilla (i know this can be edited with ctrl+w, but it is not, try it in game).

Not exactly a bug, but an effect of using the pCaster.convert(pUnit) call to give the create commander's details to the unit to which it is attached.

I changed all the great persons to start at level 6 to allow upgrades to archmages, high priests etc. Perhaps I ought to add some code similar to what I use for random sluaghs or demons summoned by priests of the veil to let the randomly named great commanders to get promotions commiserate with their level which could pass on to the unit to make up for potentially lost promotions for attaching a commander at a higher level than the unit.


Permanent error after the Stir from Slumber ritual
That is a simple typo. "spellBreathlLight" should be 'spellBreathLight" without that stray lower case l.
There's a typo, as the Temple for the One should be called Monotheist Meeting House rather than Monothiest. Nevertheless, truly great work, I really enjoy this modmod which has kept FfH alive for so many, many years!
Fixed. (And then fixed some graphical issues from using "Find in Files" "Replace with:" and changing the name of the .dds image file too, and accidentally adding a stray space.)
This seems to have resolved itself. I think maybe it's tied to techs? But if there is a tech requirement it's not listed anywhere I've seen.

Also, is The Draw a particularly reosurce/script heavy ritual? This has been awhile and on the previous version but when it finished the game hangs but doesn't freeze (spinning globe forever). What's odd is that I see everyone else declare war on me and a few cpu actions complete before it hangs.
Yeah, it as mentioned earlier it is linked to techs. I'll try to remember to document it better in the next version.
The second Auric thing isn't a bug, the tile that he is standing on gives entropy mana, the side bar does not matter for him. And yes, the draw causes everyone who is not teamed or vassaled with auric to dec war on you, this is a big AI tax as the AI starts casting spells everywhere. You could stop a bunch of the casting lag if you could somehow make the interturn go full fog so the game doesn't load the spell effects and sound off screen from tiles you have vision on but are not focused.

The Draw is pretty resource intensive not only because it forces war, but because it us checked in other places like the effectDefectAuric passive effect, as a prereq to training Drifa, to prevent Auric's enemies from building ice nodes, determining whether a Temple of the Hand will be hostile, to make Auric to insane, to determine if Frostlings from Samhain will be barbarians or belong to Auric, to increase the range of the snow effects of a temple of the hand, to cause revolts in hostile cities that have a temple of the hand or hand shrine,
The Shades have 0 attack and invisibility, and my workers disappeared without messages.
MagisterCultuum, when to expect a new version of mod? I hope not in a year.
==========
SPELL_BREATH_LIGHT spell does not give effects to own team, it should be?
I'm hoping to get a minor release out by the end of this month, but might delay if lfgr thinks a new MNAI update that prevents some crashes could be out within the next couple months.

I could not remember off the top of my head what SPELL_BREATH_LIGHT does, but looking at the code it seems it gives Courage and Morale to nearby units that have the Good alignment (unless they are HN or at war with the caster's team) and otherwise ... damages units if the caster or the caster's owner is evil? That doesn't sound right.

Kael on Reddit said:
Dawn Dragon
Dominion: Sun

These horned dragons glow with sunlight at all times, though they can control the intensity, from an angelic glow to a blinding golden light. Their breath weapon is a blast of light that burns the corrupt but won’t harm the pure. Any ally touched by the light of a Dawn Dragon is filled with incredible courage and hope. At dawn each day every Dawn Dragon is fully healed from all injuries, mental, physical or magical and is broken free of any constraints that may hold them. They collect anything made of gold.

I'm guessing I meant to make it damage the units if the target of the spell was evil or served an evil owner. Making a good aligned dragon's breath weapon harmless unless the dragon itself has turned evil or is serving an evil master is inexplicable.
edit: I just realized that iPlayer and pPlayer get redefined within the loop to match the unit instead of the caster, so it was actually doing damage if either the victim's owner or the caster was evil. That still seems wrong. I just edited it to use iPlayerU and pPlayerU instead of redefining iPlayer to reduce confusion, and to use the variable already defined for the target's unit alignment. I'm also thinking of changing this and many other spells that loop to check the distance between the caster's and target's plots, which makes the area of effect look like the shape of a city radius instead of a square.
Spoiler :

Code:
def spellBreathLight(pCaster, eSpell):
    pPlot = pCaster.plot()
    iX = pCaster.getX()
    iY = pCaster.getY()
    iPlayer = pCaster.getOwner()
    iTeam = pCaster.getTeam()
    eTeam = gc.getTeam(iTeam)
    iGood = gc.getInfoTypeForString('ALIGNMENT_GOOD')
    iEvil = gc.getInfoTypeForString('ALIGNMENT_EVIL')
    iNeutral = gc.getInfoTypeForString('ALIGNMENT_NEUTRAL')
    iX = pCaster.getX()
    iY = pCaster.getY()
    iSmoke = gc.getInfoTypeForString('IMPROVEMENT_SMOKE')
    iFlames = gc.getInfoTypeForString('FEATURE_FLAMES')
    iFire = gc.getInfoTypeForString('DAMAGE_FIRE')
    iCourage = gc.getInfoTypeForString('PROMOTION_COURAGE')
    iMorale = gc.getInfoTypeForString('PROMOTION_MORALE')
    iRange = 2
    for iiX in xrange(iX-iRange, iX + iRange+1, 1):
        for iiY in xrange(iY-iRange, iY+ iRange+1, 1):
            pPlot = CyMap().plot(iiX, iiY)
            if pPlot.isNone():continue
            # if pPlot.at(iX, iY):continue
            if plotDistance(iX, iY, iiX, iiY) > iRange:continue
            for i in xrange(pPlot.getNumUnits()):
                pUnit = pPlot.getUnit(i)
                iAlignmentU = cf.getUnitAlignment(pUnit)
                iPlayerU = pUnit.getOwner()
                pPlayerU = gc.getPlayer(iPlayer)
                if iAlignmentU == iGood:
                    if pUnit.isHiddenNationality():continue
                    if eTeam.isAtWar(pUnit.getTeam()):continue
                    if pUnit.isHasPromotion(iCourage) and pUnit.isHasPromotion(iMorale):continue
                    pUnit.setHasPromotion(iCourage, True)
                    pUnit.setHasPromotion(iMorale, True)
                elif iEvil in [pPlayerU.getAlignment(), iAlignmentU]:
                    if not effectResisted(pUnit, pCaster, eSpell):
                        pUnit.doDamage(30, 100, pCaster, iFire, False)
                        CyEngine().triggerEffect(gc.getInfoTypeForString('EFFECT_PILLAR_OF_FIRE'), pPlot.getPoint())

def reqBreathLight(pCaster, eSpell):
    pPlot = pCaster.plot()
    iX = pCaster.getX()
    iY = pCaster.getY()
    iPlayer = pCaster.getOwner()
    eTeam = gc.getTeam(pCaster.getTeam())
    iGood = gc.getInfoTypeForString('ALIGNMENT_GOOD')
    iEvil = gc.getInfoTypeForString('ALIGNMENT_EVIL')
    iNeutral = gc.getInfoTypeForString('ALIGNMENT_NEUTRAL')
    iSmoke = gc.getInfoTypeForString('IMPROVEMENT_SMOKE')
    iFlames = gc.getInfoTypeForString('FEATURE_FLAMES')
    iFire = gc.getInfoTypeForString('DAMAGE_FIRE')
    iCourage = gc.getInfoTypeForString('PROMOTION_COURAGE')
    iMorale = gc.getInfoTypeForString('PROMOTION_MORALE')
    iSmoke = gc.getInfoTypeForString('IMPROVEMENT_SMOKE')
    iFlames = gc.getInfoTypeForString('FEATURE_FLAMES')
    iRange = 2
    for iiX in xrange(iX-iRange, iX + iRange+1, 1):
        for iiY in xrange(iY-iRange, iY+ iRange+1, 1):
            pPlot = CyMap().plot(iiX, iiY)
            if pPlot.isNone():continue
            # if pPlot.at(iX, iY):continue
            if plotDistance(iX, iY, iiX, iiY) > iRange:continue
            for i in xrange(pPlot.getNumUnits()):
                pUnit = pPlot.getUnit(i)
                if pUnit.isImmuneToSpell(pCaster, eSpell):continue
                iPlayerU = pUnit.getOwner()
                pPlayerU = gc.getPlayer(iPlayer)
                iAlignmentU =cf.getUnitAlignment(pUnit)
                if iAlignmentU == iGood:
                    if pUnit.isHiddenNationality():continue
                    if eTeam.isAtWar(pUnit.getTeam()):continue
                    if pUnit.isHasPromotion(iCourage) and pUnit.isHasPromotion(iMorale):continue
                    return True
                elif iEvil in [pPlayerU.getAlignment(), iAlignmentU]:
                    if pUnit.getDamageTypeResist(iFire) < 100:
                        return True
    return False

def helpBreathLight(lpUnits, eSpell):
    szBuffer = ''
    pCaster = lpUnits[0]
    pPlot = pCaster.plot()
    iX = pCaster.getX()
    iY = pCaster.getY()
    iPlayer = pCaster.getOwner()
    eTeam = gc.getTeam(pCaster.getTeam())
    iGood = gc.getInfoTypeForString('ALIGNMENT_GOOD')
    iEvil = gc.getInfoTypeForString('ALIGNMENT_EVIL')
    iNeutral = gc.getInfoTypeForString('ALIGNMENT_NEUTRAL')
    iSmoke = gc.getInfoTypeForString('IMPROVEMENT_SMOKE')
    iFlames = gc.getInfoTypeForString('FEATURE_FLAMES')
    iFire = gc.getInfoTypeForString('DAMAGE_FIRE')
    iCourage = gc.getInfoTypeForString('PROMOTION_COURAGE')
    iMorale = gc.getInfoTypeForString('PROMOTION_MORALE')
    iSmoke = gc.getInfoTypeForString('IMPROVEMENT_SMOKE')
    iFlames = gc.getInfoTypeForString('FEATURE_FLAMES')
    listBurn = []
    listCourage = []
    iRange = 2
    for iiX in xrange(iX-iRange, iX + iRange+1, 1):
        for iiY in xrange(iY-iRange, iY+ iRange+1, 1):
            pPlot = CyMap().plot(iiX, iiY)
            if pPlot.isNone():continue
            # if pPlot.at(iX, iY):continue
            if plotDistance(iX, iY, iiX, iiY) > iRange:continue
            for i in xrange(pPlot.getNumUnits()):
                pUnit = pPlot.getUnit(i)
                if pUnit.isImmuneToSpell(pCaster, eSpell):continue
                iPlayerU = pUnit.getOwner()
                pPlayerU = gc.getPlayer(iPlayer)
                iAlignmentU =cf.getUnitAlignment(pUnit)
                if iAlignmentU == iGood:
                    if pUnit.isHiddenNationality():continue
                    if eTeam.isAtWar(pUnit.getTeam()):continue
                    if pUnit.isHasPromotion(iCourage) and pUnit.isHasPromotion(iMorale):continue
                    listCourage.append(pUnit)
                elif iEvil in [pPlayerU.getAlignment(), iAlignmentU]:
                    if pUnit.getDamageTypeResist(iFire) < 100:
                        listBurn.append(pUnit)
    if len(listCourage) > 0:
        szBuffer += helpTogglePromotionsSpecificUnits(lpUnits, listCourage, eSpell, ['PROMOTION_COURAGE','PROMOTION_MORALE'], [])
    lTargets = list(set(listBurn))
    iCount = len(lTargets)
    if iCount > 0:
        szBuffer += '\n' + localText.getText("TXT_KEY_SPELL_DAMAGE", (30, 100, ))
        if iCount == 1:
            pUnit = lTargets.pop(0)
            pPlayer = gc.getPlayer(pUnit.getOwner())
            if pPlayer != -1:
                sList = cf.getNameWithColorScheme(pUnit, pUnit.isInvisible(pCaster.getTeam(), False))
                szBuffer += localText.getText("TXT_KEY_HELP_SPELL_TARGETS_THIS", ( sList, ))
        elif iCount > 0:
            sList = ''
            while len(lTargets) > 0:
                pUnit = lTargets.pop(0)
                pPlayer = gc.getPlayer(pUnit.getOwner())
                if pPlayer != -1:
                    sList += cf.getNameWithColorScheme(pUnit, pUnit.isInvisible(pCaster.getTeam(), False))
                    if len(lTargets) > 0:
                        if len(lTargets) == 1:
                            sList += " and "
                        else:
                            sList += ", "
            szBuffer += localText.getText("TXT_KEY_HELP_SPELL_TARGETS_THESE_COUNT_LIST", (iCount, sList, ))
    return szBuffer

I had a mage (serving at the town wizard) sitting in a town with the Infernal Grimoire and he suddenly vanished. I know they can get killed when reading it, but he was under strict instructions to not open that book. Will they do that on their own sometimes or maybe the book just gobbled them up. Its nowhere to be found either.

Thanks for all of your work on this all of the little twisty bits you've added make it tons of fun.

*I found them... they were in some other city, i must have accidentally moved them there* Then I discovered a "waiting for other civilizations" loop. :( Dang. This was a neat game.

I'm not sure what is going on, but if a piece of equipment disappears I usually blame a rival Thief. The AI is surprisingly good at using their Esus priests to steal equipment. An Esus player may also have invisible hidden nationality units assassinating your units.

It is also possible that if you automated the unit or if it got a promotion such as Enraged or Paranoid that makes the unit Ai controlled that it could have pikced up the equipment and then moved somwhere else.


Angel of Death or Runewyn appear from the Tapestry House, when opponent explores the Tapestry House between my turns, they attack my opponent and the game crashes.
----------
Ring of Carcer does not spawn on map when using All Unique Features option.
----------
I can't load the birds onto the Man O'War.

What size map are you on? What is the climate like? I think I added so many unique features that there not be room to place them all on a smaller map.

The Ring of Carcer can only go on a snow or glacier tile, so if the world is too warm it may have nowhere to put it.

Maybe I should allow it on Tundras as well, to let it appear on more maps.
---


it looks like the Man O'War has
Code:
            <SpecialCargo>SPECIALUNIT_BIRD</SpecialCargo>
            <DomainCargo>DOMAIN_LAND</DomainCargo>
            <iCargo>1</iCargo>
instead of
Code:
            <SpecialCargo>SPECIALUNIT_BIRD</SpecialCargo>
            <DomainCargo>DOMAIN_LAND</DomainCargo>
            <iCargo>1</iCargo>
I guess that a unit must meet both the <SpecialCargo> and <DomainCargo> prereqs, and no units do.

I am fixed by replacing pPlot with pPlot2
Spoiler CvSpellInterface.py :

def miscastSummonBarbarian(pCaster, eSpell, iNum = 1):
pPlot = pCaster.plot()
if eSpell != -1:
iUnit = gc.getSpellInfo(eSpell).getCreateUnitType()
iNum = gc.getSpellInfo(eSpell).getCreateUnitNum()
if pCaster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_TWINCAST')):
iNum *= 2
if iUnit > -1:
pPlayer = gc.getPlayer(gc.getBARBARIAN_PLAYER())
for i in range(iNum):
pPlot2 = findClearPlot(-1, pPlot)
if pPlot2 != -1:
newUnit = pPlayer.initUnit(iUnit, pPlot2.getX(), pPlot2.getY(), UnitAITypes.UNITAI_ATTACK_CITY, DirectionTypes.DIRECTION_SOUTH)
newUnit.setDuration(2)
if not gc.getTeam(gc.getBARBARIAN_TEAM()).isAtWar(pCaster.getTeam()):
newUnit.setHasPromotion(gc.getInfoTypeForString('PROMOTION_PARANOID'), True)
for iProm in cf.getSummonPerks(pCaster):
newUnit.setHasPromotion(iProm, True)
if not newUnit.isOnlyDefensive():
newUnit.setHasPromotion(gc.getInfoTypeForString('PROMOTION_ENRAGED'), True)
if newUnit.canMoveOrAttackInto(pPlot2, False):
newUnit.attack(pPlot2, False)
Changing pPlot to pPlot2 would me a major change to the spell, as it would mean no miscast spells would result in the summon automatically attacking the caster. The unit is initialized on pPlot2 so I don't think it would ever be able to move or attack into its own plot, so if you are going to change the plot you might as well just delete those lines.
 
Last edited:
Would it be possible to hide the shared maps between turns? I believe animating everything the AI is doing could be slowing down interturns.
 
I had a deeply weird thing happen the other day. My state religion was Matronae and the Illians completed the Ascension, creating Auric Ascended. The very next turn, Auric Ascended had completely disappeared from the map and I had a new Apostate in one of my cities named "Auric Ascended" with all of the original AA's promotions. What the hell? I was at war with the Illians obviously but I was on the far side of the map from them and it didn't seem like I had done anything to cause this.
 
I had a deeply weird thing happen the other day. My state religion was Matronae and the Illians completed the Ascension, creating Auric Ascended. The very next turn, Auric Ascended had completely disappeared from the map and I had a new Apostate in one of my cities named "Auric Ascended" with all of the original AA's promotions. What the hell? I was at war with the Illians obviously but I was on the far side of the map from them and it didn't seem like I had done anything to cause this.
That does seem odd.

I think it must be caused by the arda effect Auric Ascended gets from having the Divine 2 promotion, or from the divine promotion that The Draw gives Auric before his ascension.

If a priest is somewhere where its arda modifiers are negative and if the Matronae has at least double the global influence of its own faith, and is either near a city with the Matronae religion that is controlled by a player with the Matronae state religion or is near an Apostate, then there is some code in def effectArda that may make the unit abandon its faith and its player to follow the Matronae as an Apostate instead.

Auric Ascended can move like an ICBM and could have attacked you from the opposite side of the world, when as soon as he moved to a new plot the passive spell effect ajusting his arda could trigger and lead him to defect.

I hadn't thought about this potentially applying to Avatar units. I'll fix that in the next release.

---

I had planned to have the new version out today but haven't had much chance to work on it recently.

Our air conditioner broke down and was leaking a huge amount of water, flooding the mechanical closet, a quarter of the living room, and a little of the kitchen pantry.

If it wasn't resting on a concrete slab it would have rotted out a subfloor, as that was a lot of water and there was no pan to catch it.

(It actually still did an ok job of cooling he house, which was surprising given how bad a shape it was in. It was probably 40 years old, installed around the same time as the roof we replaced back in January. The contractor said it looked like the AC would have stopped working within days and that the gas furnace had deteriorated to the point where running it this winter might have killed us.)

The living room carpet had a couple different types of mushrooms (fruiting bodies and all) growing it in and the wall at the bottom of the pantry had a lot of mold.

I ripped out half of the carpet in our house on Wednesday. (I thought about removing all of it, but did not think I would be up to moving all the heavy furniture in the dining room or other half of the living room. A couple weeks ago I hurt my back after moving furniture and ripping out carpet at a Habitat for humanity house, and couldn't stand up straight for two days. I probably exerted myself more than I should here already. My lower back isn't bothering me now but my neck and shoulders are. )

I disassembled the pantry shelves on Thursday.

We had a whole new HVAC system installed on Friday. (The new one is a bit stronger, more efficient, as had a pan with a moisture sensor cut off switch in it so it should never be able to cause flooding like the old unit did.)

On Saturday I ripped out all the moldy drywall and insulation and sprayed everything with mold armor. I also taped plastic over the door leading from the mechanical closet to the back porch, as it is a slatted door which wouldn't do much to stop hot air or bugs from getting in the house when the moldy drywall was gone. After that I realized the water heater pilot light went off when the gas was disconnected, and had to crawl through the hole where I'd removed drywall to access the water heater without needing to resecure all the plastic.

On Sunday I scrubbed the area clean and sprayed more to make sure any lingering mold is dead.

On Monday I swapped out a light switch in the mechanical closet that wouldn't work if it moved out of a very specific position about halfway between what should have been off and on. I also cut the pipes leading to some very old washing machine water line connectors, which has been sticking out from a hole in the wall for my entire life only sealed only with duct tape. I figured when needing to do drywall repairs in that area anyway I might as well path that part of the wall and make everything smooth and even. The copper pipes were rather soft and more easily crushed than cut, so I had to get someone to come and help me cap them off after mom's bed time or else wouldn't have been able to turn the water back on. After they left I decided to pull down a slightly sagging plywood ceiling over the pantry, as it would probably look better to replace those separate pieces with one piece of drywall. On top of it there was a lot of filthy old insulation, a perfectly good measuring cup, and an old rat skeleton. There was also some wiring I might be able to use to install a light fixture in there.

In the morning it should finally be dry enough to install new insulation and drywall.
 
1692137519984.png


Ran into this little baby trying to let Auric gobble up brigit. Happens every time i roll over the button. I can send a save if need be.

The game i'm in is at 1000 turns so far.
 
Magister, could you remove the minimum four tile gap between cities (i.e. back to the BTS standard of three)? I find it really irritating to try and dotmap cities in, especially on diagonals.
 
Love the mod - tons of cool stuff.

I haven't run into anything game-breaking that couldn't be fixed with the WB.
I'd like to add some of what I have seen here.

I encountered the issue with the Fertility spell and creation mana described by Samson in this post.
After losing some life mana I had several cities at -100% food.
The workaround was to use the WB to add the life mana, remove fertility, wait a turn, remove the life mana, re-add fertility.

I've also seen some really strange behavior from some of the AI. Some Examples:
  1. Sheaim never researched Mining despite settling in an area with tons of forests. As a result, he is working mostly unimproved tiles.
    This is on turn 276. The other civs have most of the tech tree completed at this point.
    Spoiler :
    Civ IV_ Beyond The Sword 9_17_2023 1_59_04 PM.png
  2. The AI loves to stack units on graveyards.
    Turn 175, Kuriotates have about 70 warriors stacked in this graveyard on their borders. There are other examples of this in the same game, but this is the most extreme
    Spoiler :
    Civ IV_ Beyond The Sword 9_17_2023 2_17_46 PM.png
 
i have tried many times to make this mod work on the installer but i keep running into an error that says an error occured while trying to read the source file: the source file "\mods/Fall from Heaven 2\*" does not exist.
 
After Alekseyev_ commented here about the AI being blocked from using the Escape spell when it would make the most sense to do so, I got to thinking about changing how the Escape in my modmod.

The behavior of having summons escape to their summoner (which would also make vampires escape to their sires etc) doesn't really seem to add much to the game, so I'll probably cut it.

What would you think if I made the spell no longer teleport the unit to its owner's capital city, but instead teleport it to whatever the closest city (excluding any city on the caster's own plot) controlled by your team is? Going to the closest team or player city would be equally easy.

The code for this is fairly simple, in some ways simpler than the way I had the spell working.
Spoiler :
Code:
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
    iTeam = pCaster.getTeam()
    eTeam = gc.getTeam(iTeam)
    if eTeam.getNumCities() > 0:
        iX = pCaster.getX()
        iY = pCaster.getY()
        pPlot = pCaster.plot()
        bCoastalOnly = pCaster.getDomainType() == gc.getInfoTypeForString('DOMAIN_SEA')
        pSkipCity = pPlayer.getCity(-1)
        if pPlot.isCity():
            pSkipCity = pPlot.getPlotCity()
        pCity = CyMap().findCity(iX, iY, PlayerTypes.NO_PLAYER, iTeam, False, bCoastalOnly, TeamTypes.NO_TEAM, DirectionTypes.NO_DIRECTION, pSkipCity)
        if pCity != -1 and not pCity.isNone():
            return True
    return False

def reqEscapeFacilitated(pCaster, eSpell=1):
    if pCaster.isHasPromotion(gc.getInfoTypeForString('PROMOTION_DIMENSIONAL1')):
        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)
    iTeam = pCaster.getTeam()
    eTeam = gc.getTeam(iTeam)
    if eTeam.getNumCities() > 0:
        iX = pCaster.getX()
        iY = pCaster.getY()
        bCoastalOnly = pCaster.getDomainType() == gc.getInfoTypeForString('DOMAIN_SEA')
        pSkipCity = pPlayer.getCity(-1)
        pPlot = pCaster.plot()
        if pPlot.isCity():
            pSkipCity = pPlot.getPlotCity()
        pCity = CyMap().findCity(iX, iY, PlayerTypes.NO_PLAYER, iTeam, False, bCoastalOnly, TeamTypes.NO_TEAM, DirectionTypes.NO_DIRECTION, pSkipCity)
        if pCity != -1 and not pCity.isNone():
            pCaster.setXY(pCity.getX(), pCity.getY(), False, True, True)

def helpEscape(lpUnits, eSpell=1):
    szBuffer = ''
    pCaster = lpUnits[0]
    iPlayer = pCaster.getOwner()
    pPlayer = gc.getPlayer(iPlayer)
    iTeam = pCaster.getTeam()
    eTeam = gc.getTeam(iTeam)
    if eTeam.getNumCities() > 0:
        pCity = -1
        pSkipCity = pPlayer.getCity(-1)
        pPlot = pCaster.plot()
        if pPlot.isCity():
            pSkipCity = pPlot.getPlotCity()
        iX1 = pCaster.getX()
        iY1 = pCaster.getY()
        sNameRefuge = ''
        for pCaster in lpUnits:
           
            iX2 = -1
            iY2 = -1
            bCoastalOnly = pCaster.getDomainType() == gc.getInfoTypeForString('DOMAIN_SEA')
            pCity = CyMap().findCity(iX1, iY1, PlayerTypes.NO_PLAYER, iTeam, False, bCoastalOnly, TeamTypes.NO_TEAM, DirectionTypes.NO_DIRECTION, pSkipCity)
            if pCity != -1 and not pCity.isNone():
                pExitPlot = pCity.plot()
                iX2 = pCity.getX()
                iY2 = pCity.getY()
                sNameRefuge ="<color=%d,%d,%d,%d>%s</color>" %(pPlayer.getPlayerTextColorR(), pPlayer.getPlayerTextColorG(), pPlayer.getPlayerTextColorB(), pPlayer.getPlayerTextColorA(), pCity.getName())
                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

I suppose I could make multiple versions of the escape spell too, one for the capital and one for the closest friendly city, but the game is already a bit cluttered with duplicate greater/weaker/facilitated versions.

II don't see any reason to stop the Barbarian state from using it if it doesn't rely on having a capital city anymore.



View attachment 669909

Ran into this little baby trying to let Auric gobble up brigit. Happens every time i roll over the button. I can send a save if need be.

The game i'm in is at 1000 turns so far.
The parameter pCaster should have been lpUnits. That is a very easy fix which of course will be in the next version.
Magister, could you remove the minimum four tile gap between cities (i.e. back to the BTS standard of three)? I find it really irritating to try and dotmap cities in, especially on diagonals.
MIN_CITY_RANGE is already at 3. BtS default is 2. I guess I could put it back to that. I don't like the AI crowding that much though. I guess I could use python to block AI cities at that distance only, but it could mess up already bad AI behavior.
Love the mod - tons of cool stuff.

I haven't run into anything game-breaking that couldn't be fixed with the WB.
I'd like to add some of what I have seen here.

I encountered the issue with the Fertility spell and creation mana described by Samson in this post.
After losing some life mana I had several cities at -100% food.
The workaround was to use the WB to add the life mana, remove fertility, wait a turn, remove the life mana, re-add fertility.

I've also seen some really strange behavior from some of the AI. Some Examples:
  1. Sheaim never researched Mining despite settling in an area with tons of forests. As a result, he is working mostly unimproved tiles.
    This is on turn 276. The other civs have most of the tech tree completed at this point.
  2. The AI loves to stack units on graveyards.
    Turn 175, Kuriotates have about 70 warriors stacked in this graveyard on their borders. There are other examples of this in the same game, but this is the most extreme
Thanks.

Not sure why the bonuses from building having mana work like that. It might count as a DLL bug I'd need lfgr to look into.

Perhaps I should just remove the mana-based bonuses, but I do like the idea of building spells scaling with mana like affinity.

Is waiting a turn necessary to fix the problem?

If not, I might be able to fix the issue by placing this under def onCityDoTurn(self, argsList) to automate your solution.
Code:
        for iBuilding in xrange(gc.getNumBuildingInfos()):
            if pCity.getNumBuilding(iBuilding):
                infoBuilding = gc.getBuildingInfo(iBuilding)
                if infoBuilding.isRequiresCaster():
                    iBonus = infoBuilding.getPrereqAndBonus()
                    if iBonus != -1:
                        if pCity.getNumBonuses(iBonus) < 1:
                            pCity.changeFreeBonus(iBonus, 1)
                            pCity.setNumRealBuilding(iBuilding, False)
                            pCity.changeFreeBonus(iBonus, -1)
                            pCity.setNumRealBuilding(iBuilding, True)


1. Not sure about the Sheaim AI. I'm not an expert on AI behavior. Maybe lfgr would be a better person to ask.


2 is almost certainly due to the Graveyard improvement having the <bExploreTarget>1</bExploreTarget> tag, which the AI uses to know to send units to explore the sites of lairs or in this case to rob graves. The issue is that I changed the PyPreq for the Rob Grave spell, which in base FFH2/MNAI only blocks the barbarians from robbing graves. For thematic/lore reasons I did not want the Eternal Cabal or players to go around robbing graves, or for good players to defile Hallowed Ground on Graveyards, or for AI good players to rob graves at all. The tag is thus making Ai players send units there not not letting them do anything. I guess I should change it to <bExploreTarget>0, although that might make evil AI players less good at robbing graves.
Spoiler :

Code:
def reqRobGrave(pCaster, eSpell=-1):
    if pCaster.isBarbarian():
        return False
    if pCaster.getReligion() == gc.getInfoTypeForString('RELIGION_ETERNAL_CABAL'):
        return False
    if pCaster.getUnitType() in [gc.getInfoTypeForString('UNIT_TOMB_WARDEN'),gc.getInfoTypeForString('UNIT_ANGEL_OF_DEATH')]:
        return False
    iPlayer = pCaster.getOwner()
    pPlayer = gc.getPlayer(iPlayer)
    if pPlayer.getStateReligion() == gc.getInfoTypeForString('RELIGION_ETERNAL_CABAL'):
        return False
    if pCaster.plot().getFeatureType() == gc.getInfoTypeForString('FEATURE_HALLOWED_GROUND'):
        if pCaster.getRace() in [gc.getInfoTypeForString('PROMOTION_DEMON'), gc.getInfoTypeForString('PROMOTION_UNDEAD')]:
            return False
        if pPlayer.getAlignment() != gc.getInfoTypeForString('ALIGNMENT_EVIL'):
            return False
    if not pCaster.isHuman():
        if pPlayer.getAlignment() == gc.getInfoTypeForString('ALIGNMENT_GOOD'):
            if pCaster.getCivilizationType() != gc.getInfoTypeForString('CIVILIZATION_MERCURIANS'):
                return False
        if pPlayer.getCivilizationType() == gc.getInfoTypeForString('CIVILIZATION_SIDAR'):
            return False
    return True



i have tried many times to make this mod work on the installer but i keep running into an error that says an error occured while trying to read the source file: the source file "\mods/Fall from Heaven 2\*" does not exist.
Do you have the normal FfH2 mod installed already? The installer should find it and copy it automatically before overwriting some of it with the changed files, but there can be issued with the installer finding the right place for the mods folder in different versions of windows or on Steam versions of Civ IV.


You may just have to rely on the archive version instead of the installer. Manually copy the FfH2 folder, rename it, extract the contents of the ZIP, and copy it into the renamed copy.
 
Last edited:
Not sure why the bonuses from building having mana work like that. It might count as a DLL bug I'd need lfgr to look into.

Perhaps I should just remove the mana-based bonuses, but I do like the idea of building spells scaling with mana like affinity.

Is waiting a turn necessary to fix the problem?
I didn't see Fertitlity in the Buildings list in the WB, so the only way to remove it was by moving the spellcaster out, waiting a turn for it to deconstruct, then moving him back in to re-build.
If you can forcibly remove the building I suppose waiting a turn wouldn't be necessary?

Also, just to be clear, this issue can happen when you still have > 0 life mana.
I went from 3 life mana to 1 and all my cities began to starve.
 
I've already tried that and it just loaded up with the normal ffh2 but I'm probably just doing it wrong. I also cant seem to work out how to extract the file and folder structure.
Do you have the normal FfH2 mod installed already? The installer should find it and copy it automatically before overwriting some of it with the changed files, but there can be issued with the installer finding the right place for the mods folder in different versions of windows or on Steam versions of Civ IV.


You may just have to rely on the archive version instead of the installer. Manually copy the FfH2 folder, rename it, extract the contents of the ZIP, and copy it into the renamed copy.
 
I didn't see Fertitlity in the Buildings list in the WB, so the only way to remove it was by moving the spellcaster out, waiting a turn for it to deconstruct, then moving him back in to re-build.
If you can forcibly remove the building I suppose waiting a turn wouldn't be necessary?

Also, just to be clear, this issue can happen when you still have > 0 life mana.
I went from 3 life mana to 1 and all my cities began to starve.


In order to see the spell buildings you must first turn off the "Hide Inactive" toggle (the second from the right on the third row)
1696511670382.png



And then select either the "Spell Buildings" or "All" option on the drop down filter menu in the Buildings screen.

1696511650530.png



I'm just not seeing the behavior you described. Losing mana is making the city lose the extra food from Fertility, which may leave it without enough to sustain a large population that had come to rely on the mana food bonus, but I'm not seeing the bonus go negative.
 
Greetings, everyone! I decided to tell a little about a couple of chips that I use all the time.

1. Playing as Ammurites, you can pump Metamagic 1 and dimensional 1 for adepts to acquire Soaring Eye and Escape, respectively.
We set the city and constantly leaving the city adepts spam Escape until the failure of this spell, which takes us to a random point on the map. With the help of Soaring Eye we open a significant area of the map around the adept. When the area has been explored, we return to the capital via Escape and repeat until the theoretically complete open map. By turn 50 this is theoretically possible.

Plus. Great exploration potential from the start of the game (I would even say unfair in some outcomes).
Minus. Adepts are extremely vulnerable and from turn 10-20 are a tidbit for barbarians.

2. Playing for the Illians and with the completion of the White Hand ritual to found the city of New Malir. Look for a source of mana near the city, and if it is Enchantment mana, you are already a winner. Due to Auric's peculiarity of casting Direction 3 spells on a Enchantment mana source, he starts producing Golden Hammers every turn, which he takes to the nearest city, accelerating production to infinity.

Plus. Infinite production!!!
Minus. Instability and long execution to which you have to live. Near the city most likely will not be Mana Enchantment. In the worst case, you will have to research Magic technology to dispel the source and put with Mana Enchantment. Also makes Great Engineers appear 99% of the time, which is not always necessary for a particular party
P.s. I hate to say it but 2 tip needs to be nerfed(
 
Top Bottom