[MOD] MagisterModmod

Note that the scenarios for this modmod are found in PrivateMaps (which is necessary if you want the option to load them as Custom Scenarios from the main menu), not in Assets\XML\Scenarios where Kael hid them away. The way the installer works those scenarios remain there and are not overwritten, so they are almost certainly broken
Just quickly- yes these are the versions I use which don't work. Worth noting I have to use the manual install (copy pasting the filea) as the installer doesn't work, in case that's at all related?

I always play with worldspells on, so that could well be the issue.
 
What triggers the end of the arrogating trait? After some time you no longer can use the conquered city as from another civ and it behaves as your own. It does not seem to be dependent just on time or cultural %. Is it a combination of factors or is it something else?
 
What triggers the end of the arrogating trait? After some time you no longer can use the conquered city as from another civ and it behaves as your own. It does not seem to be dependent just on time or cultural %. Is it a combination of factors or is it something else?
I am afraid I have no idea, but welcome to CFC :beer::band::dance::beer:
 
Hi all! I got the idea to make a small stained glass window on Fall from Heaven 2 like in Heroes of Might and Magic 5,6,7, and here are the sketches. It is planned to insert the Mobius witch, Emir, the Dream Eater and .... possibly the Abashi dragon, but this is more for the dragon cult than the Middle or Sheim. How do you like the idea? Do you have any ideas what can be implemented here? ВИтраж.png
 

Attachments

  • post-1-0-90934500-1437044124.jpg
    post-1-0-90934500-1437044124.jpg
    403.6 KB · Views: 66
  • igra-geroi-geroi-mecha-i-magii-geroi-mecha-i-magii-7-heroes.jpg
    igra-geroi-geroi-mecha-i-magii-geroi-mecha-i-magii-7-heroes.jpg
    388.2 KB · Views: 65
  • Screenshot_2.png
    Screenshot_2.png
    2.3 MB · Views: 70
Hi all! I got the idea to make a small stained glass window on Fall from Heaven 2 like in Heroes of Might and Magic 5,6,7, and here are the sketches. It is planned to insert the Mobius witch, Emir, the Dream Eater and .... possibly the Abashi dragon, but this is more for the dragon cult than the Middle or Sheim. How do you like the idea? Do you have any ideas what can be implemented here?View attachment 632249
All I shall say is that this is truly awesome.
 
I have been getting another weird error lately:

upload_2022-6-26_15-44-56.png


At first I noticed it when I selected an artificier or a mage with enchantment, but now I see the error pop up more and more often, allmost as if it was ... contagious.
Do you know what it means and how it can be resolved?
 
I have been getting another weird error lately:

View attachment 632306

At first I noticed it when I selected an artificier or a mage with enchantment, but now I see the error pop up more and more often, allmost as if it was ... contagious.
Do you know what it means and how it can be resolved?

This is the bug that I found andposted a fix for the day after the last version was released
I just found my first post-release bug a couple minutes ago. I'm not sure how it eluded me, as it was in a spell I haven't touched in several weeks and was sure was working before.

In the Repair spell, I somehow omitted or deleted the definition of the Retrofit spell used in order to make the Ringgiver's Repair spell also upgrade Golems to gain the promotions from the Luchuirp unique buildings.

The code should be this:
Spoiler :

Code:
def reqRepair(pCaster, iAmount = 30, eSpell=-1):
    pPlot = pCaster.plot()
    sRetrofit = 'SPELL_RETROFIT_GOLEM'
    iGolem = gc.getInfoTypeForString('PROMOTION_GOLEM')
    iRusted = gc.getInfoTypeForString('PROMOTION_RUSTED')
    lCombats = [gc.getInfoTypeForString('UNITCOMBAT_NAVAL'), gc.getInfoTypeForString('UNITCOMBAT_SIEGE')]
    for i in xrange(pPlot.getNumUnits()):
        pUnit = pPlot.getUnit(i)
        if pUnit.getDuration() > 0:continue
        if pUnit.isHasPromotion(iRusted):
            return True
        iRace = pUnit.getRace()
        if iRace == iGolem:
            if reqRetrofitGolem(pUnit, sRetrofit):
                return True
            if pUnit.getDamage() > 0:
                return True
        if iRace == iGolem:
            return True
        elif iRace == -1:
            if pUnit.getUnitCombatType() in lCombats:
                if pUnit.getDamage() > 0:
                    return True
    return False
def spellRepair(pCaster, iAmount = 30, eSpell=-1):
    pPlot = pCaster.plot()
    sRetrofit = 'SPELL_RETROFIT_GOLEM'
    iRusted = gc.getInfoTypeForString('PROMOTION_RUSTED')
    iGolem = gc.getInfoTypeForString('PROMOTION_GOLEM')
    lCombats = [gc.getInfoTypeForString('UNITCOMBAT_NAVAL'), gc.getInfoTypeForString('UNITCOMBAT_SIEGE')]
    for i in xrange(pPlot.getNumUnits()):
        pUnit = pPlot.getUnit(i)
        if pUnit.getDuration() > 0:continue
        if pUnit.getDamage() == 0:continue
        iRace = pUnit.getRace()
        if iRace == iGolem:
            pUnit.changeDamage(-iAmount, PlayerTypes.NO_PLAYER)
            pUnit.setHasPromotion(iRusted, False)
            if reqRetrofitGolem(pUnit, sRetrofit):
                spellRetrofitGolem(pUnit, sRetrofit)
        elif iRace == -1:
            if pUnit.getUnitCombatType() in lCombats:
                pUnit.changeDamage(-iAmount, PlayerTypes.NO_PLAYER)
                pUnit.setHasPromotion(iRusted, False)
def helpRepair(lpUnits, iAmount = 30, eSpell=-1):
    szBuffer = ''
    pCaster = lpUnits[0]
    iCount = 0
    lTargets= []
    lRusted = []
    lRetrofit = []
    pPlot = pCaster.plot()
    sRetrofit = gc.getInfoTypeForString('SPELL_RETROFIT_GOLEM')
    iRusted = gc.getInfoTypeForString('PROMOTION_RUSTED')
    iGolem = gc.getInfoTypeForString('PROMOTION_GOLEM')
    lCombats = [gc.getInfoTypeForString('UNITCOMBAT_NAVAL'), gc.getInfoTypeForString('UNITCOMBAT_SIEGE')]
    for i in xrange(pPlot.getNumUnits()):
        pUnit = pPlot.getUnit(i)
        if pUnit.getDuration() > 0:continue
        if pUnit.isHasPromotion(iRusted):
            lRusted.append(pUnit)
        iRace = pUnit.getRace()
        if iRace == iGolem:
            if reqRetrofitGolem(pUnit, sRetrofit):
                lRetrofit.append(pUnit)
            elif pUnit.getDamage() == 0:continue
            iCount += 1
            lTargets.append(pUnit)
        elif iRace == -1:
            if pUnit.getDamage() == 0:continue
            if pUnit.getUnitCombatType() in lCombats:
                iCount += 1
                lTargets.append(pUnit)
    lTargets = list(set(lTargets))
    if len(lRetrofit) > 0:
        szBuffer += helpRetrofitGolem(lRetrofit, sRetrofit)
    iCount = len(lRusted)
    if iCount > 0:
        szBuffer += helpTogglePromotionsSpecificUnits(lRusted, eSpell, [], ['PROMOTION_RUSTED'], False)
    iCount = len(lTargets)
    if iCount > 0:
        if iCount == 1:
            pUnit = lTargets.pop(0)
            pPlayer = gc.getPlayer(pUnit.getOwner())
            sList = "<color=%d,%d,%d,%d>%s</color>" %(pPlayer.getPlayerTextColorR(), pPlayer.getPlayerTextColorG(), pPlayer.getPlayerTextColorB(), pPlayer.getPlayerTextColorA(), pUnit.getName() )
            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())
                sList += "<color=%d,%d,%d,%d>%s</color>" %(pPlayer.getPlayerTextColorR(), pPlayer.getPlayerTextColorG(), pPlayer.getPlayerTextColorB(), pPlayer.getPlayerTextColorA(), pUnit.getName() )
                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
edit: I at first posted a wrong correction, thinking that eSpell in spellRetrofitGolem(pCaster, eSpell), reqRetrofitGolem(pCaster, eSpell), and helpRetrofitGolem(lpUnits, eSpell) should haave been an index instead of a text string.

edit2: I noticed the python error when the game froze up and thought it was related, but unfortunately it still seems to hang at that same point despite the changes. Also, the eSpell parameter of the Retrofit Golem spell is entirely optional and never actually gets used for anything, so you could just use eSpell, -1, or omit the parameter altogether instead of definine sRetrofit.
 
Thanks a lot for taking the time to answer my question.
However, since I don't know much about the technical aspect of modding, I am still a bit confused about what to do.

- Is the code that you posted actually correct and usable (i.e. does it incorporate the comments from you two edits? or does it need to be further modified in some way?)
- Where (in what file) does that code need to be inserted?
- If this was discussed before, can you please tell me on which page of this thread it was originally posted? (maybe there are further comments that I could read to figure out the answer by myself)
 
This mod would be much better if its creator would upload small patches, rather than doing a big update once a year and expecting everyone to read every post in the thread to see what bugs have been identified. That code that was just posted isn't even mentioned in the edited original post.

Yes, I understand that the mod is more of a personal thing than other mods; but it is publicly posted presumably so people will try it; and those problems disincentivize me from trying it again.
 
Aaand... it's the end of the line for my current game. I made it until turn 361 when it eventually stopped. Once I press the end turn button the game becomes completely unresponsive. There is no error message and no discernible cause for this.
 
This is probably not a bug but is probably not intended either: When I upgrade a Vagrant to an Adept, it gains one free promotion as usual. Now, I can cast Vagrancy and turn it back to a Vagrant. However, in the next turn I can upgrade it to an Adept again, which makes it gain another free promotion. This probably shouldn't be allowed? Since I can just have it buy all the possible promotions while sitting in the capital in 10+ or 20+ turns.
 
I don't know if this bug report has appeared before, but my game crashes when the "Animal Revolt" ritual ends. Save attached +4 Turn
.
 

Attachments

  • AutoSave_ХОД-0220.CivBeyondSwordSave
    281.5 KB · Views: 16
I don't know if this bug report has appeared before, but my game crashes when the "Animal Revolt" ritual ends. Save attached +4 Turn
.
I don't think I'd had such a report before, but it was very helpful.

I read it while experiencing the same issue in my current game, and think it could have been the cause of problems in several previous games too where the logs looked similar but I never found the issue.

My current game kept freezing. I noticed in BBAILog - Magister.txt that the last thing seemed to be the death in python of some goblins, so I tried using worldbuilding to kill all the goblins, but then the same thing just happened with Lizardmen. Then warriors, etc. I couldn't figure out what was going on, but when I read your post I noticed that Cardith Lords was prepared to finish "Nature's Revolt" that turn.

When I tried triggering Natures Revolt in worldbuilder I had the same issue without waiting for the turn to end.


I looked in CvEventManager.py and found the code is this:
Code:
        elif iProjectType == gc.getInfoTypeForString('PROJECT_NATURES_REVOLT'):
            lHeroicPromotions = [    gc.getInfoTypeForString('PROMOTION_HEROIC_DEFENSE'),
                        gc.getInfoTypeForString('PROMOTION_HEROIC_DEFENSE2'),
                        gc.getInfoTypeForString('PROMOTION_HEROIC_STRENGTH'),
                        gc.getInfoTypeForString('PROMOTION_HEROIC_STRENGTH2')
                        ]
##            iAnimal = gc.getInfoTypeForString('UNITCOMBAT_ANIMAL')
            iAxeman = gc.getInfoTypeForString('UNITCLASS_AXEMAN')
            iBear = gc.getInfoTypeForString('UNIT_BEAR')
            iHeld = gc.getInfoTypeForString('PROMOTION_HELD')
            iHunter = gc.getInfoTypeForString('UNITCLASS_HUNTER')
            iLion = gc.getInfoTypeForString('UNIT_LION')
            iScout = gc.getInfoTypeForString('UNITCLASS_SCOUT')
            iTiger = gc.getInfoTypeForString('UNIT_TIGER')
            iWarrior = gc.getInfoTypeForString('UNITCLASS_WARRIOR')
            iWolf = gc.getInfoTypeForString('UNIT_WOLF')
            iWorker = gc.getInfoTypeForString('UNITCLASS_WORKER')
            iBeast = gc.getInfoTypeForString('UNITCOMBAT_BEAST')
            for iLoopPlayer in xrange(gc.getMAX_PLAYERS()):
                pLoopPlayer = gc.getPlayer(iLoopPlayer)
                if pLoopPlayer.isAlive():
                    (pUnit, iter) = pLoopPlayer.firstUnit(False)
                    while(pUnit):
                        if ( not pUnit.isDead() ): #is the unit alive and valid?
                            if pUnit.isAlive():
                                if pUnit.isAnimal():
                                    for iProm in lHeroicPromotions:
                                        pUnit.setHasPromotion(iProm, True)
                                elif pUnit.isBarbarian():
                                    if pUnit.isHasPromotion(iHeld) or pUnit.getUnitCombatType() == iBeast:
                                        continue
                                    bValid = False
                                    iUnitType = pUnit.getUnitClassType()
                                    if iUnitType == iWorker:
                                        iNewUnit = iWolf
                                        bValid = True
                                    elif iUnitType == iScout:
                                        iNewUnit = iLion
                                        bValid = True
                                    elif iUnitType == iWarrior:
                                        iNewUnit = iLion
                                        bValid = True
                                    elif iUnitType == iHunter:
                                        iNewUnit = iTiger
                                        bValid = True
                                    elif iUnitType == iAxeman:
                                        iNewUnit = iBear
                                        bValid = True
                                    if bValid:
                                        newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                                        newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                                        newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                                        pUnit.kill(True, PlayerTypes.NO_PLAYER)
                        (pUnit, iter) = pLoopPlayer.nextUnit(iter, False)
            for iUnit in [gc.getInfoTypeForString('UNIT_GURID'), gc.getInfoTypeForString('UNIT_MARGALARD'), gc.getInfoTypeForString('UNIT_LEVIATHAN'), gc.getInfoTypeForString('UNIT_XIEN')]:
                if CyGame().getUnitCreatedCount(iUnit) == 0:
                    cf.addUnit(iUnit)
I did not see an obvious source of the problem, but I decided to compare with vanilla FfH2 and saw that it uses for pUnit in py.getUnitList(): instead of a while loop.

I decided to bring my mode closer in line with Kael's, using this

Code:
        elif iProjectType == gc.getInfoTypeForString('PROJECT_NATURES_REVOLT'):
            lHeroicPromotions = [    gc.getInfoTypeForString('PROMOTION_HEROIC_DEFENSE'),
                        gc.getInfoTypeForString('PROMOTION_HEROIC_DEFENSE2'),
                        gc.getInfoTypeForString('PROMOTION_HEROIC_STRENGTH'),
                        gc.getInfoTypeForString('PROMOTION_HEROIC_STRENGTH2')
                        ]
##            iAnimal = gc.getInfoTypeForString('UNITCOMBAT_ANIMAL')
            iAxeman = gc.getInfoTypeForString('UNITCLASS_AXEMAN')
            iBear = gc.getInfoTypeForString('UNIT_BEAR')
            iHeld = gc.getInfoTypeForString('PROMOTION_HELD')
            iHunter = gc.getInfoTypeForString('UNITCLASS_HUNTER')
            iLion = gc.getInfoTypeForString('UNIT_LION')
            iScout = gc.getInfoTypeForString('UNITCLASS_SCOUT')
            iTiger = gc.getInfoTypeForString('UNIT_TIGER')
            iWarrior = gc.getInfoTypeForString('UNITCLASS_WARRIOR')
            iWolf = gc.getInfoTypeForString('UNIT_WOLF')
            iWorker = gc.getInfoTypeForString('UNITCLASS_WORKER')
            iBeast = gc.getInfoTypeForString('UNITCOMBAT_BEAST')
            bPlayer = gc.getPlayer(gc.getBARBARIAN_PLAYER())
            py = PyPlayer(gc.getBARBARIAN_PLAYER())
            for pUnit in py.getUnitList():
                if pUnit.isAlive():
                    if pUnit.isAnimal():
                        for iProm in lHeroicPromotions:
                            pUnit.setHasPromotion(iProm, True)
                    elif pUnit.isBarbarian():
                        if pUnit.isHasPromotion(iHeld) or pUnit.getUnitCombatType() == iBeast:
                            continue
                        bValid = False
                        iUnitType = pUnit.getUnitClassType()
                        if iUnitType == iWorker:
                            iNewUnit = iWolf
                            bValid = True
                        elif iUnitType == iScout:
                            iNewUnit = iLion
                            bValid = True
                        elif iUnitType == iWarrior:
                            iNewUnit = iLion
                            bValid = True
                        elif iUnitType == iHunter:
                            iNewUnit = iTiger
                            bValid = True
                        elif iUnitType == iAxeman:
                            iNewUnit = iBear
                            bValid = True
                        if bValid:
                            newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                            newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                            newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                            pUnit.kill(True, PlayerTypes.NO_PLAYER)
                          
            for iUnit in [gc.getInfoTypeForString('UNIT_GURID'), gc.getInfoTypeForString('UNIT_MARGALARD'), gc.getInfoTypeForString('UNIT_LEVIATHAN'), gc.getInfoTypeForString('UNIT_XIEN')]:
                if CyGame().getUnitCreatedCount(iUnit) == 0:
                    cf.addUnit(iUnit)
With that code the ritual caused no problems whether triggered in worldbuilder or completed normally.


That above code only effects the Barbarian player, however, whereas I had a long time ago changed it to try to effect all animals owned by anyone. If you want it to do that but not freeze or chash the game, I think this is what we should use insead:
Code:
        elif iProjectType == gc.getInfoTypeForString('PROJECT_NATURES_REVOLT'):
            lHeroicPromotions = [    gc.getInfoTypeForString('PROMOTION_HEROIC_DEFENSE'),
                                    gc.getInfoTypeForString('PROMOTION_HEROIC_DEFENSE2'),
                                    gc.getInfoTypeForString('PROMOTION_HEROIC_STRENGTH'),
                                    gc.getInfoTypeForString('PROMOTION_HEROIC_STRENGTH2')
                                    ]
            iAxeman = gc.getInfoTypeForString('UNITCLASS_AXEMAN')
            iBear = gc.getInfoTypeForString('UNIT_BEAR')
            iHeld = gc.getInfoTypeForString('PROMOTION_HELD')
            iHunter = gc.getInfoTypeForString('UNITCLASS_HUNTER')
            iLion = gc.getInfoTypeForString('UNIT_LION')
            iScout = gc.getInfoTypeForString('UNITCLASS_SCOUT')
            iTiger = gc.getInfoTypeForString('UNIT_TIGER')
            iWarrior = gc.getInfoTypeForString('UNITCLASS_WARRIOR')
            iWolf = gc.getInfoTypeForString('UNIT_WOLF')
            iWorker = gc.getInfoTypeForString('UNITCLASS_WORKER')
          
            py = PyPlayer(gc.getBARBARIAN_PLAYER())
            for pUnit in py.getUnitList():
                if not pUnit.isAlive():
                    continue
                if pUnit.isHasPromotion(iHeld):
                    continue
                if pUnit.isAnimal():
                    for iProm in lHeroicPromotions:
                        pUnit.setHasPromotion(iProm, True)
                    continue
                bValid = False
                iUnitType = pUnit.getUnitClassType()
                if iUnitType == iWorker:
                    iNewUnit = iWolf
                    bValid = True
                elif iUnitType == iScout:
                    iNewUnit = iLion
                    bValid = True
                elif iUnitType == iWarrior:
                    iNewUnit = iLion
                    bValid = True
                elif iUnitType == iHunter:
                    iNewUnit = iTiger
                    bValid = True
                elif iUnitType == iAxeman:
                    iNewUnit = iBear
                    bValid = True
                if bValid:
                    newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                    newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                    newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                    pUnit.kill(True, PlayerTypes.NO_PLAYER)
            for iLoopPlayer in xrange(gc.getMAX_CIV_PLAYERS()):
                pLoopPlayer = gc.getPlayer(iLoopPlayer)
                if pLoopPlayer.isAlive():
                    py = PyPlayer(iLoopPlayer)
                    for pUnit in py.getUnitList():
                        if pUnit.isAlive():
                            if pUnit.isAnimal():
                                for iProm in lHeroicPromotions:
                                    pUnit.setHasPromotion(iProm, True)
            for iUnit in [gc.getInfoTypeForString('UNIT_GURID'), gc.getInfoTypeForString('UNIT_MARGALARD'), gc.getInfoTypeForString('UNIT_LEVIATHAN'), gc.getInfoTypeForString('UNIT_XIEN')]:
                if CyGame().getUnitCreatedCount(iUnit) == 0:
                    cf.addUnit(iUnit)


I think a long time ago I decided that using those while loops ought to be more efficient than calling py.getUnitList(), but I'm not sure it is really true or if so is very significant.

def getUnitList(self): itself just uses such a while loop, but it may make a big difference whether you make a list of al units before or after doing things to each unit.

Sometimes it may be more efficient to act on each unit in the while loop, like if you are only interested in finding a specific unit and want to break the loop as soon as you find it rather than going on to check any of the other units (e.g., when the Ascension ritual needs to find Auric in order to turn him into Auric Ascended).

It is however a very bad idea to use a while loop if you are deleting units or adding more units while continuing to cycle through them, as is done in his ritual. It could risk infinite loops or cause some units to get skipped.

I'm thinking I should go through each of the places I switched to such while looks and change them back to getUnitList unless I can think of a good reason not to in that particular case. It would make the code look cleaner and easier to read, even when it does not fix bugs.

I'm also thinking that I'd like to change this ritual to make a wider variety of animals. A bunch of wolves and bears is not as cool as panthers, elephants, baboons, gorillas, pegasi, etc.
 
Last edited:
I'm not sure if this helps but I distinctly remember successfully completing the "Nature's revolt" ritual when playing with this mod about a year ago, without immediately crashing the game. So maybe whatever code is causing the crash was added during the last few updates.
 
Aaand... it's the end of the line for my current game. I made it until turn 361 when it eventually stopped. Once I press the end turn button the game becomes completely unresponsive. There is no error message and no discernible cause for this.

As I have seen it suggested in this thread before, I checked the logs from before the game freezes and these are the final lines of the log:

Spoiler :

City Garduk finishes production of building Public Baths
City Garduk needs more defense!
City Garduk pushes production of unit Ghoul with UNITAI attack
City Garduk uses choose UNITAI_ATTACK (minimal troops: 3)
City Garduk finishes production of unit Ghoul (3678236) with UNITAI attack
City Garduk needs more defense!
City Garduk pushes production of unit Ghoul with UNITAI attack
City Garduk uses choose UNITAI_ATTACK (minimal troops: 3)
City Garduk finishes production of unit Ghoul (3686520) with UNITAI attack
City Garduk needs more defense!
City Garduk pushes production of unit Ghoul with UNITAI attack
City Garduk uses choose UNITAI_ATTACK (minimal troops: 3)
City Garduk finishes production of unit Ghoul (3694713) with UNITAI attack
City Garduk needs more defense!
City Garduk pushes production of unit Ghoul with UNITAI attack
City Garduk uses choose UNITAI_ATTACK (minimal troops: 3)
City Garduk finishes production of unit Ghoul (3702906) with UNITAI attack
...
{repeated ad infinitum}
....

(the city Garduk is controlled by the Calabim and this happened during their turn.

I have seen the suggestion of entering the worldbuilder in such cases.

Does anyone more knowlegeable know what is happening here and what needs to be changed in order to prevent the game from freezing?
 
Hi all! About two months ago, I came up with an idea that, in my opinion, should bring a little control to the random component of the game. The idea is that at the beginning of the game we get a randomly selected mana set in our faction's palace, instead of a pre-made mana set.
So that it does not look like a boring script output, you can present it in the form of a mini story or a prologue to the beginning of the game.

Let's look at the example of Elohim.

1. As soon as we place a city, the first text box appears in which the player is told a brief (or not) prehistory of our civilization. This text should mention that our people are blessed by Sirona, the Goddess of Wisdom, and grant you a piece of her power in the form of Soul Mana.

2. On the next turn, a second text box appears, which mentions the current leader of the faction (For example, Einion Logos), his background as led by the people, etc. And at the end of the window, his strengths, capabilities and knowledge in a particular area of magic are mentioned (I don’t know how to say, not all leaders are magicians), which ultimately gives us a sphere of magic that depends on a particular leader (it is different for everyone). For the Logos, it can be... The same Magic of the Soul or Metamagic. (Nothing prevents you from taking one more sphere of magic from the patron god).

3. On the next turn, the most interesting thing happens. The third window comes to us in which it is said about the Elohim people that they "survived the Age of Ice and are now ready to go into a new Age of Rebirth with a desire ...

1. ...create something new!" (Magic of Creation)
2. ... to shed light on the mysteries of this world!" (Magic of the Sun)
3. ...put this world in order!" (Order Magic)
4. ...to temper faith in the best!" (Magic Enchantment)
5. ...ensure the long life of your nation!" (Life Magic)
6. ...gain peace of mind!" (Soul Magic)
7. ...bring your people to prosperity!" (Nature Magic)
8. ...finish what you started?????? (didn't figure out how to mention Death magic and Undead magic in the same sentence)
9. ...acquire new knowledge!" (Metamagic Magic)
10. ...take a dip." (Water Magic, but I'd change the sentence...)
11. ...provide a stable existence for the world." (Magic of Strength)
12. ...dig up new riches." (Earth Magic)
13. ...to discover new and unknown lands." (Wind Magic)
14. ...stop... any... resistance..." (Ice Magic)
15. ... figure out how to get rich at the expense of others!" (Mind Magic)
16. ...DRIVE THIS WORLD IN ETERNAL Slaughter!!!" (Magic of Chaos)(letters can be arranged in a chaotic order))))
17. ...show the world your power (smirk)." (Body Magic)
18. ...ignite... Inner FLAME!!!" (Fire Magic)
19. ... point out to everyone their shortcomings..." (Magic of Entropy)
20. ...divide and destroy everyone." (Dimensional Magic)
21. ...hide from everyone..." (Shadow Magic)

As soon as you choose it from the offers, you get that kind of mana. You can take any previously received mana (from god and leader). As a limitation, you can make a rule that you can’t take magic that is opposite in worldview from your patron god or a set of opposite magics (Soul Magic is the School of Divination, which means you can’t take the school of Necromancy), but you can take adjacent ones (the School of Elementalism or Change). Most likely this rule will not be used as it breaks the mechanics of some nations such as Sidar or Amurites, but it is worth suggesting.

P.s. I believe that adding this function as an additional option when creating a new custom game will allow for a more flexible start based on the local location of opponents, resources and mana sources.

P.s.№2. The idea of this function came to my mind after a burnt ass from standing around the desert due to the lack of water magic and raw mana sources around while playing not for Malakim)
 
Last edited:
I don't think I'd had such a report before, but it was very helpful.

I read it while experiencing the same issue in my current game, and think it could have been the cause of problems in several previous games too where the logs looked similar but I never found the issue.

My current game kept freezing. I noticed in BBAILog - Magister.txt that the last thing seemed to be the death in python of some goblins, so I tried using worldbuilding to kill all the goblins, but then the same thing just happened with Lizardmen. Then warriors, etc. I couldn't figure out what was going on, but when I read your post I noticed that Cardith Lords was prepared to finish "Nature's Revolt" that turn.

When I tried triggering Natures Revolt in worldbuilder I had the same issue without waiting for the turn to end.


I looked in CvEventManager.py and found the code is this:
Code:
        elif iProjectType == gc.getInfoTypeForString('PROJECT_NATURES_REVOLT'):
            lHeroicPromotions = [    gc.getInfoTypeForString('PROMOTION_HEROIC_DEFENSE'),
                        gc.getInfoTypeForString('PROMOTION_HEROIC_DEFENSE2'),
                        gc.getInfoTypeForString('PROMOTION_HEROIC_STRENGTH'),
                        gc.getInfoTypeForString('PROMOTION_HEROIC_STRENGTH2')
                        ]
##            iAnimal = gc.getInfoTypeForString('UNITCOMBAT_ANIMAL')
            iAxeman = gc.getInfoTypeForString('UNITCLASS_AXEMAN')
            iBear = gc.getInfoTypeForString('UNIT_BEAR')
            iHeld = gc.getInfoTypeForString('PROMOTION_HELD')
            iHunter = gc.getInfoTypeForString('UNITCLASS_HUNTER')
            iLion = gc.getInfoTypeForString('UNIT_LION')
            iScout = gc.getInfoTypeForString('UNITCLASS_SCOUT')
            iTiger = gc.getInfoTypeForString('UNIT_TIGER')
            iWarrior = gc.getInfoTypeForString('UNITCLASS_WARRIOR')
            iWolf = gc.getInfoTypeForString('UNIT_WOLF')
            iWorker = gc.getInfoTypeForString('UNITCLASS_WORKER')
            iBeast = gc.getInfoTypeForString('UNITCOMBAT_BEAST')
            for iLoopPlayer in xrange(gc.getMAX_PLAYERS()):
                pLoopPlayer = gc.getPlayer(iLoopPlayer)
                if pLoopPlayer.isAlive():
                    (pUnit, iter) = pLoopPlayer.firstUnit(False)
                    while(pUnit):
                        if ( not pUnit.isDead() ): #is the unit alive and valid?
                            if pUnit.isAlive():
                                if pUnit.isAnimal():
                                    for iProm in lHeroicPromotions:
                                        pUnit.setHasPromotion(iProm, True)
                                elif pUnit.isBarbarian():
                                    if pUnit.isHasPromotion(iHeld) or pUnit.getUnitCombatType() == iBeast:
                                        continue
                                    bValid = False
                                    iUnitType = pUnit.getUnitClassType()
                                    if iUnitType == iWorker:
                                        iNewUnit = iWolf
                                        bValid = True
                                    elif iUnitType == iScout:
                                        iNewUnit = iLion
                                        bValid = True
                                    elif iUnitType == iWarrior:
                                        iNewUnit = iLion
                                        bValid = True
                                    elif iUnitType == iHunter:
                                        iNewUnit = iTiger
                                        bValid = True
                                    elif iUnitType == iAxeman:
                                        iNewUnit = iBear
                                        bValid = True
                                    if bValid:
                                        newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                                        newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                                        newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                                        pUnit.kill(True, PlayerTypes.NO_PLAYER)
                        (pUnit, iter) = pLoopPlayer.nextUnit(iter, False)
            for iUnit in [gc.getInfoTypeForString('UNIT_GURID'), gc.getInfoTypeForString('UNIT_MARGALARD'), gc.getInfoTypeForString('UNIT_LEVIATHAN'), gc.getInfoTypeForString('UNIT_XIEN')]:
                if CyGame().getUnitCreatedCount(iUnit) == 0:
                    cf.addUnit(iUnit)
I did not see an obvious source of the problem, but I decided to compare with vanilla FfH2 and saw that it uses for pUnit in py.getUnitList(): instead of a while loop.

I decided to bring my mode closer in line with Kael's, using this

Code:
        elif iProjectType == gc.getInfoTypeForString('PROJECT_NATURES_REVOLT'):
            lHeroicPromotions = [    gc.getInfoTypeForString('PROMOTION_HEROIC_DEFENSE'),
                        gc.getInfoTypeForString('PROMOTION_HEROIC_DEFENSE2'),
                        gc.getInfoTypeForString('PROMOTION_HEROIC_STRENGTH'),
                        gc.getInfoTypeForString('PROMOTION_HEROIC_STRENGTH2')
                        ]
##            iAnimal = gc.getInfoTypeForString('UNITCOMBAT_ANIMAL')
            iAxeman = gc.getInfoTypeForString('UNITCLASS_AXEMAN')
            iBear = gc.getInfoTypeForString('UNIT_BEAR')
            iHeld = gc.getInfoTypeForString('PROMOTION_HELD')
            iHunter = gc.getInfoTypeForString('UNITCLASS_HUNTER')
            iLion = gc.getInfoTypeForString('UNIT_LION')
            iScout = gc.getInfoTypeForString('UNITCLASS_SCOUT')
            iTiger = gc.getInfoTypeForString('UNIT_TIGER')
            iWarrior = gc.getInfoTypeForString('UNITCLASS_WARRIOR')
            iWolf = gc.getInfoTypeForString('UNIT_WOLF')
            iWorker = gc.getInfoTypeForString('UNITCLASS_WORKER')
            iBeast = gc.getInfoTypeForString('UNITCOMBAT_BEAST')
            bPlayer = gc.getPlayer(gc.getBARBARIAN_PLAYER())
            py = PyPlayer(gc.getBARBARIAN_PLAYER())
            for pUnit in py.getUnitList():
                if pUnit.isAlive():
                    if pUnit.isAnimal():
                        for iProm in lHeroicPromotions:
                            pUnit.setHasPromotion(iProm, True)
                    elif pUnit.isBarbarian():
                        if pUnit.isHasPromotion(iHeld) or pUnit.getUnitCombatType() == iBeast:
                            continue
                        bValid = False
                        iUnitType = pUnit.getUnitClassType()
                        if iUnitType == iWorker:
                            iNewUnit = iWolf
                            bValid = True
                        elif iUnitType == iScout:
                            iNewUnit = iLion
                            bValid = True
                        elif iUnitType == iWarrior:
                            iNewUnit = iLion
                            bValid = True
                        elif iUnitType == iHunter:
                            iNewUnit = iTiger
                            bValid = True
                        elif iUnitType == iAxeman:
                            iNewUnit = iBear
                            bValid = True
                        if bValid:
                            newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                            newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                            newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                            pUnit.kill(True, PlayerTypes.NO_PLAYER)
                        
            for iUnit in [gc.getInfoTypeForString('UNIT_GURID'), gc.getInfoTypeForString('UNIT_MARGALARD'), gc.getInfoTypeForString('UNIT_LEVIATHAN'), gc.getInfoTypeForString('UNIT_XIEN')]:
                if CyGame().getUnitCreatedCount(iUnit) == 0:
                    cf.addUnit(iUnit)
With that code the ritual caused no problems whether triggered in worldbuilder or completed normally.


That above code only effects the Barbarian player, however, whereas I had a long time ago changed it to try to effect all animals owned by anyone. If you want it to do that but not freeze or chash the game, I think this is what we should use insead:
Code:
        elif iProjectType == gc.getInfoTypeForString('PROJECT_NATURES_REVOLT'):
            lHeroicPromotions = [    gc.getInfoTypeForString('PROMOTION_HEROIC_DEFENSE'),
                                    gc.getInfoTypeForString('PROMOTION_HEROIC_DEFENSE2'),
                                    gc.getInfoTypeForString('PROMOTION_HEROIC_STRENGTH'),
                                    gc.getInfoTypeForString('PROMOTION_HEROIC_STRENGTH2')
                                    ]
            iAxeman = gc.getInfoTypeForString('UNITCLASS_AXEMAN')
            iBear = gc.getInfoTypeForString('UNIT_BEAR')
            iHeld = gc.getInfoTypeForString('PROMOTION_HELD')
            iHunter = gc.getInfoTypeForString('UNITCLASS_HUNTER')
            iLion = gc.getInfoTypeForString('UNIT_LION')
            iScout = gc.getInfoTypeForString('UNITCLASS_SCOUT')
            iTiger = gc.getInfoTypeForString('UNIT_TIGER')
            iWarrior = gc.getInfoTypeForString('UNITCLASS_WARRIOR')
            iWolf = gc.getInfoTypeForString('UNIT_WOLF')
            iWorker = gc.getInfoTypeForString('UNITCLASS_WORKER')
        
            py = PyPlayer(gc.getBARBARIAN_PLAYER())
            for pUnit in py.getUnitList():
                if not pUnit.isAlive():
                    continue
                if pUnit.isHasPromotion(iHeld):
                    continue
                if pUnit.isAnimal():
                    for iProm in lHeroicPromotions:
                        pUnit.setHasPromotion(iProm, True)
                    continue
                bValid = False
                iUnitType = pUnit.getUnitClassType()
                if iUnitType == iWorker:
                    iNewUnit = iWolf
                    bValid = True
                elif iUnitType == iScout:
                    iNewUnit = iLion
                    bValid = True
                elif iUnitType == iWarrior:
                    iNewUnit = iLion
                    bValid = True
                elif iUnitType == iHunter:
                    iNewUnit = iTiger
                    bValid = True
                elif iUnitType == iAxeman:
                    iNewUnit = iBear
                    bValid = True
                if bValid:
                    newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                    newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                    newUnit = bPlayer.initUnit(iNewUnit, pUnit.getX(), pUnit.getY(), UnitAITypes.NO_UNITAI, DirectionTypes.DIRECTION_NORTH)
                    pUnit.kill(True, PlayerTypes.NO_PLAYER)
            for iLoopPlayer in xrange(gc.getMAX_CIV_PLAYERS()):
                pLoopPlayer = gc.getPlayer(iLoopPlayer)
                if pLoopPlayer.isAlive():
                    py = PyPlayer(iLoopPlayer)
                    for pUnit in py.getUnitList():
                        if pUnit.isAlive():
                            if pUnit.isAnimal():
                                for iProm in lHeroicPromotions:
                                    pUnit.setHasPromotion(iProm, True)
            for iUnit in [gc.getInfoTypeForString('UNIT_GURID'), gc.getInfoTypeForString('UNIT_MARGALARD'), gc.getInfoTypeForString('UNIT_LEVIATHAN'), gc.getInfoTypeForString('UNIT_XIEN')]:
                if CyGame().getUnitCreatedCount(iUnit) == 0:
                    cf.addUnit(iUnit)


I think a long time ago I decided that using those while loops ought to be more efficient than calling py.getUnitList(), but I'm not sure it is really true or if so is very significant.

def getUnitList(self): itself just uses such a while loop, but it may make a big difference whether you make a list of al units before or after doing things to each unit.

Sometimes it may be more efficient to act on each unit in the while loop, like if you are only interested in finding a specific unit and want to break the loop as soon as you find it rather than going on to check any of the other units (e.g., when the Ascension ritual needs to find Auric in order to turn him into Auric Ascended).

It is however a very bad idea to use a while loop if you are deleting units or adding more units while continuing to cycle through them, as is done in his ritual. It could risk infinite loops or cause some units to get skipped.

I'm thinking I should go through each of the places I switched to such while looks and change them back to getUnitList unless I can think of a good reason not to in that particular case. It would make the code look cleaner and easier to read, even when it does not fix bugs.

I'm also thinking that I'd like to change this ritual to make a wider variety of animals. A bunch of wolves and bears is not as cool as panthers, elephants, baboons, gorillas, pegasi, etc.

I dont know how to "update" my game with this. Im doing "ctrl C ctrl V" the exactly lines in the exactly archive (civeventmanager.py), and the game starts without UI.
Sem título.jpg
 
whos the female on the right ? The big one behind looks like a ballor.

The woman on the right is a random woman who was raped, torn apart, mutilated, and had her dead flesh sewn onto a large golem for the purpose of "enchanting" the fighters during battle. Also, a detachment of archers, swordsmen, magicians (not drawn) has already been sewn to the golem.
 
Top Bottom