1. We have added a Gift Upgrades feature that allows you to gift an account upgrade to another member, just in time for the holiday season. You can see the gift option when going to the Account Upgrades screen, or on any user profile screen.
    Dismiss Notice

MagisterModmod

Discussion in 'FfH2 Modmods, Scenarios, and Maps' started by MagisterCultuum, Feb 27, 2012.

  1. Guru-Meditation

    Guru-Meditation Chieftain

    Joined:
    Oct 9, 2010
    Messages:
    81
    Yay! New release!
     
  2. civerfey

    civerfey Chieftain

    Joined:
    Jun 8, 2017
    Messages:
    12
    Thanks for the fixes Woad. I am getting a lot of CTD too, repeatable on a single turn. I have only been able to temporarily stop them by going into World Builder and deleting civs by process of elimination although i'm not convinced it was a specific civ it seemed to give me a handful more turns. My third crash stopped my game at turn 717 of a Marathon game. Applied your fixes but still getting that crash just after the Barbs finish their turn.
     
  3. MagisterCultuum

    MagisterCultuum Great Sage

    Joined:
    Feb 14, 2007
    Messages:
    15,860
    Location:
    Kael's head
    I am planning to release another new update for Valentines day, but I still have things to playtest so I am not promising it will be ready tomorrow.

    It will mostly be minor bug fixes, improved help strings, etc.

    I added a python prereq to the Water Walking spells so that it won't let you cast it on units that are immune to PROMOTION_WATER_WALKING_TEMP or which get no benefit from it as they can already move through water. The PyHelp is updated to exclude those units too.

    Pillar of Fire's PyHelp now tells you what tile will be targeted. I had trouble tracking down a bug in the code to make it tell you what units are targeted, but since it damages everything on a specific tile identifying the tile seemed good enough.

    The PyHelp for Escape now tell you the name of the city or summoner to which the unit will be moved, and below that describes how many tiles the caster will be moved in each direction.

    Shadowwalk now lets units enter the Whispering Woods.

    I made Hallowed Ground impassible to various demons and undead units in xml (those units that have those races in the xml defines rather than having a race applies as a civ's default or in python), which should be easier for the AI to understand and a bit more efficient than python.

    The Summon Imp spell now has Sorcery as a tech prereq, but no longer requires a Mage Guild and can be cast outside of cities by living units with Unholy Taint and unlimited duration. Imps summoned on mana sources of course are more likely to start knowing a spell in that sphere.

    I decided that letting Rings of Warding block all hostile summons (and Greater Rings of Warding also block elementals, demons, angels, and undead) was too powerful. Now those units may overcome that effect if they are of a high enough level. The rule as currently implemented is that a unit's level must be more than double the Force mana supply for a normal Ring of Warding and more than triple for a grater one.


    I tweaked the code of Whiteout spell allowed by Snow Stealth because I started to suspect that losing hidden nationality in the middle of attacking neutral units, or gaining hidden nationality while other members of the group do not, could have been the cause of some persistent crashes. After this change some of my repeatable crashing saved games were able to continue without a crash, but others were not.

    I restricted Hollow Men so that they cannot move through Grass or Plains anymore, but are restricted to colder or more hellish terrain.

    The number free promotion picks granted by the Towers of the Elements, Necromancy, and Divination grant free promotion picks to Elementals, Demons, and Angels with mana prereqs is now randomized, with a minimum of zero and a maximum equal to the effective mana supply for the unit's prerequisite mana. Note that Pyre Zombies and Hollow Men get their free promotion picks from the Tower of Necromancy based on how much Fire mana or Ice mana you control, not Death mana like the rest of the undead.

    Djinns now have Channeling 3 whether summoned by the lesser or greater Summon Djinn spell. The greater version has Mastery of Magic, so it may use its free promotion picks to purchase Twincast, Summoner, or various Affinities.

    Mass Resurrection removes the limited duration from any hostile undead summons that it brings back to life.

    Life Affinity + Channeling 3 only gives Life 3 and Immortal to units without limited duration. You cannot just summon a bunch of Djinns to resurrect your heroes, or have them be born again when they lose in combat. (Edit: I just realized I am not sure that this check runs before the duration is applied, so it might only matter to greater Djinns purchasing Life affinity with their Mastery of Magic.)

    Mastery of Magic can still wear off, but I no longer automatically remove it after every new affinity is purchased.

    The Crown of Akharien can randomly grant Mastery of Magic.

    Sluaghs generated from Graveyards now usually have limited duration, so the armies they allow won't grow too large. Spellstaffs are no longer found with Sluaghs of arcane units, as it was weird seeing so many copies of that equipment showing up on graveyards as slaughs pass out of Erebus.

    The free spell spheres or affinities previously given to arcane units only when they are created are now applied when they gain the Channeling 1 or 2 promotions.

    Govanon's Ethics can only teach Channeling to units with the proper minimum level, but can teach 1st level spell spheres to units without Channeling. Units that know spells do not need channeling to teach those spells to others. The PyHelp function seems to be working now, except that I have not found a way (that is not terribly messy) to stop it from saying that it may teach a promotion to units that are immune to that promotion due to their other promotions. This is mostly noticed when it says that a student of Govannon still on the same tile as Govannon may teach Govannon Metamagic 1, despite his Metamagic affinity making that useless and impossible.


    The Puppet promotion now has a PyPerTurn effect that updates the Puppet so that it can always access whatever spells its summoner knows that turn, not only whatever the summoner knew at the time when the puppet itself was summoned. Puppets cannot get Affinity, however. They instead get the normal 1st, 2nd, and 3rd level spells (dependent on channeling promotions) in the spheres for which their summoners have affinity.

    Adventurers loose affinities and channeling promotions when they revert from an arcane unit back to the adventurer unit. They loose promotions like inquisitor and evangelist when they revert from disciple units. The adventurer promotion increases the chance of the unit withdrawing from combat, increases the unit's effective supply of Air mana (Tali is the patron of Adventurers), and gives a 90% reduction in upgrade costs.

    The Caswallawn promotion may be purchased with experience by a level 12+ arcane unit with Channeling 3, rather than only granted from Cave Trial. Taking the promotion still triggers a duel to the death against any previous Caswallawn.

    I corrected a mistake that was preventing Vitalize from creating New Forests/Forests/Ancient Forests if the caster has Nature Affinity.

    I am thinking of (but have not yet coded) making the Greater version of the Bloom spells create New Forests/Forests/Ancient Forests on Adjacent tiles instead of only working on the same tile as the normal version of the spell but a turn faster.

    If I do so I may simply the Vitalize spell by just making it also trigger Greater Bloom, and maybe also Greater Spring to get an Oasis in the desert.

    I am reconsidering whether Air should be a sphere Druids can access.


    Right now I am playing around with making the White Hand ritual transform Letum Frigus into a new city called New Mulyr. (The Holy Shrine of The White Hand, previously called New Mulyr, is now called The Throne of Hell. I might let it double as an obsidian gate, letting you airlift one unit per turn in case Auric needs to escape.)

    The Letum Frigus improvement is automatically removed when I add a city, but the Ice mana remains on the tile so Auric can use Ice affinity there. I could use python to add Letum Frigus back, but I believe that would add unnecessary complications like exploring a lair resulting in a barbarian unit capturing the city automatically.

    I would like Letum Frigus to be left behind if and when when New Mulyr is ever razed. Razing any city results in City Ruins being placed there, after the onCityRazed code finished running so I cannot check on anything about the city directly. I instead added a check for whenever new City Ruins are applied to a plot containing Ice mana after the White Hand ritual has been completed and there is no Letum Frigus elsewhere in the world.

    At first I was thinking Letum Frigus would become a city only if Auric does not have any other cities. The point was to prevent the common issue of Auric and his army sitting within Letum Frigus and never founding a city or doing anything because they cannot move through rival territory, or because all the good city sites have been taken. Now I am thinking I may just give them a city there regardless.

    I just made it so that settlers cannot settle on tiles adjacent to Letum Frigus. I am also thinking of making Letum Frigus generate Hollow Men, like Barrows generate Skeletons, in order to help make sure it remains a wilderness and is not too crowded with cities before Auric finds it. (If I did put Letum Frigus back on the tile along with a city then these barbarian Hollow Men might capture the city before I could do anything like use python to switch them to Auric's control.)
     
    Imp. Knoedel likes this.
  4. Calavente

    Calavente Richard's voice

    Joined:
    Jun 4, 2006
    Messages:
    2,728
    Location:
    France
    thanks.

    really interesting

    two questions:
    -currently Amurite and Illian have a running race toward Letum Frigus. If it generates hollow men, Amurite will have a much harder time to get the bonus with their early scouts..

    -would a spell giving "needs lvl X to enter tile" (X = 2, 3 4 ?) be possible ? or is it only available to improvements ? If yes, what spell sphere would correspond thematically ? (I thought that great Ward could work with "need lvl 3 to enter tile" so that you could protect a city from swarms... or 1 turn summons, until the archmage is assassinated...)
     
    Last edited: Feb 14, 2019
  5. MagisterCultuum

    MagisterCultuum Great Sage

    Joined:
    Feb 14, 2007
    Messages:
    15,860
    Location:
    Kael's head
    I've never seen why the Amurites should have any special attraction to this feature. Sure, Kylorin fought there, but it was always hostile territory. Canonically it is haunted by Hollow Men, who killed everyone who tried to explore it in the time between Kylorin leaving and Auric arriving.
    It would actually be very easy for a python spell to make a tile impassible to all units below a certain level, using the same mechanic ( pPlot.setMinLevel(i) ) that blocks units from entering the Ring of Carcer in vanilla FfH2.

    The code is so simple that you don't even have to write it in CvSpellInterface.py but could simply place a single line like <PyResult>pCaster.plot().setMinLevel(pCaster.getLevel())</PyResult> in CIV4SpellInfos.xml.

    Such an effect would best fit the Force sphere. In fact, it is very much like my original idea for the Ring of Warding, which I originally wanted to make an improvement rather than a building. I have also considered making the Greater Ring of Warding an improvement so that units with Force Affinity may cast the spell almost anywhere (I'd probably have to prevent its use on unique features) whereas Force 2 only allows it to be cast in cities.

    Using pPlot.setMinLevel(i) should be significantly more efficient and let the game run faster than using def unitCannotMoveInto(self,argsList): like I do for Rings of Warding now.

    One problem with this approach is that it would apply to all units below such a level, without exceptions for friendly units or for Runewyns (which canonically can pass through any magical barrier). I could give Runewyns a special spell to remove adjacent Rings of Warding, but I'm not sure it is the best idea to prevent your own team's units from entering a tile. I think we'd at least need another spell to remove the Ring of Warding temporarily in order to let friendly reinforcements arrive before casting it again.



    Another problem comes if you do not want that limitation to be permanent. It could get tricky to try to check the tile whenever the caster moves away (including the use of Dimensional magic to move it into non-adjacent tiles) in order to reset the level requirement.

    You could cycle through every tile on the map every turn, or even every time a unit moves, in order to reset the minimum levels of all tiles without a caster present. That would be so inefficient the game would be rendered unplayable.

    A better approach would be to add a new improvement or feature which has a <PythonAtRange> effect that checks the tile for a potential caster of that spell and then clears the improvement and the minimum level if such a caster cannot be found on that tile. It could also adjust the plot's minimum level if you want to the formula to be something more complicated than some constant integer. For example, if the rule is that Force Affinity casters can block only units whose level is lower than their own and also lower than their effective supply of Force mana, then you could use this:
    Code:
    
    def atRangeUpdateWarding(pCaster, pPlot):
       iLevel = 0
       iAffinity = gc.getInfoTypeForString('PROMOTION_AFFINITY_FORCE')
       iMana = gc.getInfoTypeForString('BONUS_MANA_FORCE')
       for i in xrange(pPlot.getNumUnits()):
           pUnit = pPlot.getUnit(i)
           if pUnit.isHasPromotion(iAffinity):
               iLevel = max(iLevel, min(pUnit.getLevel(), cf.getNumBonusEffective(pUnit.getOwner(), iMana, pUnit) ) )
       pPlot.setMinLevel(iLevel)
       if iLevel == 0:
           pPlot.setImprovementType(-1)
    

    An advantage of using an improvement is that you could add just 2 lines of code in def onImprovementDestroyed(self, argsList): which would remove the level prereq whenever the improvement is removed by any method, including removing it in worldbuilder.
    Code:
    
               if iImprovement == gc.getInfoTypeForString('IMPROVEMENT_RING_OF_WARDING'):
                   pPlot.setMinLevel(0)
    You could use the Temp Feature or Temp Improvement mechanics if you want to make a Ring of Warding that only lasts a specific number of turns and then makes the tile revert to whatever feature or improvement was on the tile previously.
    Code:
           pPlot.setTempImprovementType(gc.getInfoTypeForString('IMPROVEMENT_RING_OF_WARDING'), min(pCaster.getLevel(), cf.getNumBonusEffective(pCaster.getOwner(), iMana, pUnit) ) )
    One option would be to make the Greater Ring of Warding spell produce a temporary Ring of Warding improvement with a temporary timer of 1, and have the Force Affinity promotion increment that to 2 (but never past 2) through a PyPerTurn effect. That way there would be no need for a <PythonAtRange>

    Now that I write that though I realize that the easiest way to make Runewyns immune to Rings of Warding would be to make the <PythonAtRange> call remove the improvement whenever a runewyn approaches regardless of what units are on the tile, or if any hostile units are on the tile.






    I have written out enough of this code while answering your question that I think I want to playtest it now. That makes it very unlikely than I will release an update today.

    Edit: I just realized you were concerned specifically with protecting cities from hostile swarms of low level units, not with letting casters use the spell out in the field. What you want is a much more minor change, especially just removing the checks that prevent Greater Ring of Warding from working on mundane low level units. That should be easy.

    The code currently looks like this:
    Code:
    
       def unitCannotMoveInto(self,argsList):
           ePlayer = argsList[0]
           iUnitId = argsList[1]
           pUnit = gc.getPlayer(ePlayer).getUnit(iUnitId)
    
           if not pUnit.isAlive():
               pPlot = CyMap().plot(argsList[2], argsList[3])
               if pPlot.getTeam() != pUnit.getTeam():
    
                   if pPlot.isOwned():
                       #The compact requires that the gods' prevent their greater servants from directly harming those whose souls they don't already own
                       #Balors and the avatar units of each Infernal leader, before the AC 100 event
                       if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_BOUND_BY_COMPACT')):
                           if gc.getTerrainInfo(pPlot.getTerrainType()).getTerrainDown() == -1:#i.e., is not Hell terrain
                               if gc.getPlayer(pPlot.getOwner()).getStateReligion() != gc.getInfoTypeForString('RELIGION_THE_ASHEN_VEIL'):
                                   return True
                       if pPlot.isCity():#eliminates superforts that would count as isEnemyCity, since for some reason forts were being counted as having every building
                           if pPlot.isEnemyCity(pUnit):#The plot has a city or superfort whose owner is at war with unit's owner, or the unit is hidden nationality.
                               #The Ring of Warding spell-building prevents hostile summons from entering/attacking the city
                               if not pUnit.isImmuneToMagic(): #Runewyns are immune to and can dispell Rings of Warding
    ##                               if pUnit.isHiddenNationality() or gc.getTeam(pUnit.getTeam()).isAtWar(pPlot.getTeam()):
                                   pCity = pPlot.getPlotCity()
                                   if pCity.getNumBuilding(gc.getInfoTypeForString('BUILDING_SHRINE_OF_ARAWN')):
                                       if pUnit.getRace() == gc.getInfoTypeForString('PROMOTION_UNDEAD'):
                                           if pUnit.getLevel()//3 < pCity.getNumBonuses(gc.getInfoTypeForString('BONUS_MANA_DEATH')):
                                               return True
                                   if pCity.getNumBuilding(gc.getInfoTypeForString('BUILDING_RING_OF_WARDING_GREATER')):
                                       if pUnit.getSummoner() != -1 or pUnit.isPermanentSummon() or pUnit.getDuration() > 0 or pUnit.getRace() in [gc.getInfoTypeForString('PROMOTION_UNDEAD'), gc.getInfoTypeForString('PROMOTION_ELEMENTAL'),gc.getInfoTypeForString('PROMOTION_ANGEL'), gc.getInfoTypeForString('PROMOTION_DEMON')]:
                                           if pUnit.getLevel()//3 < pCity.getNumBonuses (gc.getInfoTypeForString('BONUS_MANA_FORCE')):
                                               return True
                                   elif pCity.getNumBuilding(gc.getInfoTypeForString('BUILDING_RING_OF_WARDING')):
                                       if pUnit.getSummoner() != -1 or pUnit.isPermanentSummon() or pUnit.getDuration() > 0:
                                           if pUnit.getLevel()//2 < pCity.getNumBonuses(gc.getInfoTypeForString('BONUS_MANA_FORCE')):
                                               return True
    
                   elif pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_BOUND_BY_COMPACT')):
                       if gc.getTerrainInfo(pPlot.getTerrainType()).getTerrainDown() == -1:#i.e., is not Hell terrain
                               return True
                   if pPlot.isPythonActive():
                       if pUnit.isAutomated() or not pUnit.isHuman():
                           if pPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_HALLOWED_GROUND'):
                               if pUnit.getRace() in [gc.getInfoTypeForString('PROMOTION_DEMON'),gc.getInfoTypeForString('PROMOTION_UNDEAD')]:
                                   return False
                           iImprovement = pPlot.getImprovementType()
                           if iImprovement == gc.getInfoTypeForString('IMPROVEMENT_TARCHS_TOWER'):
                               if not pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_CHANNELING2')):
                                   return True
                           elif iImprovement == gc.getInfoTypeForString('IMPROVEMENT_MAELSTROM'):
                               if not pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_AFFINITY_AIR')):
                                   return True
                           elif iImprovement == gc.getInfoTypeForString('IMPROVEMENT_MIRROR_OF_HEAVEN'):
                               if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_VAMPIRE')):
                                   return True
                               elif pUnit.getUnitClassType() == gc.getInfoTypeForString('UNITCLASS_SHADOWRIDER'):
                                   return True
                           elif iImprovement == gc.getInfoTypeForString('IMPROVEMENT_WHISPERING_WOOD'):
                               if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_DEASIL_CHARM')):
                                   pass
                               elif pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_SHADOWWALK')):
                                   pass
                               elif pUnit.getUnitClassType() == gc.getInfoTypeForString('UNITCLASS_AURIC'):
                                   pass
                               else:
                                   return True
           return False
    I think what you want is more like this:
    Code:
        def unitCannotMoveInto(self,argsList):
           ePlayer = argsList[0]
           iUnitId = argsList[1]
           pUnit = gc.getPlayer(ePlayer).getUnit(iUnitId)
    
           pPlot = CyMap().plot(argsList[2], argsList[3])
           if pPlot.getTeam() != pUnit.getTeam():
    
               if pPlot.isOwned():
                   #The compact requires that the gods' prevent their greater servants from directly harming those whose souls they don't already own
                   #Balors and the avatar units of each Infernal leader, before the AC 100 event
                   if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_BOUND_BY_COMPACT')):
                       if gc.getTerrainInfo(pPlot.getTerrainType()).getTerrainDown() == -1:#i.e., is not Hell terrain
                           if gc.getPlayer(pPlot.getOwner()).getStateReligion() != gc.getInfoTypeForString('RELIGION_THE_ASHEN_VEIL'):
                               return True
                   if pPlot.isCity():#eliminates superforts that would count as isEnemyCity, since for some reason forts were being counted as having every building
                       if pPlot.isEnemyCity(pUnit):#The plot has a city or superfort whose owner is at war with unit's owner, or the unit is hidden nationality.
                           #The Ring of Warding spell-building prevents hostile summons from entering/attacking the city
                           if not pUnit.isImmuneToMagic(): #Runewyns are immune to and can dispell Rings of Warding
    ##                               if pUnit.isHiddenNationality() or gc.getTeam(pUnit.getTeam()).isAtWar(pPlot.getTeam()):
                               pCity = pPlot.getPlotCity()
                               if pCity.getNumBuilding(gc.getInfoTypeForString('BUILDING_RING_OF_WARDING_GREATER')):
                                   if pUnit.getLevel()//3 < pCity.getNumBonuses(gc.getInfoTypeForString('BONUS_MANA_FORCE')):
                                       return True
                               elif not pUnit.isAlive():
                                   if pCity.getNumBuilding(gc.getInfoTypeForString('BUILDING_RING_OF_WARDING')):
                                       if pUnit.getSummoner() != -1 or pUnit.isPermanentSummon() or pUnit.getDuration() > 0:
                                           if pUnit.getLevel()//2 < pCity.getNumBonuses(gc.getInfoTypeForString('BONUS_MANA_FORCE')):
                                               return True
                                   if pCity.getNumBuilding(gc.getInfoTypeForString('BUILDING_SHRINE_OF_ARAWN')):
                                       if pUnit.getRace() == gc.getInfoTypeForString('PROMOTION_UNDEAD'):
                                           if pUnit.getLevel()//3 < pCity.getNumBonuses(gc.getInfoTypeForString('BONUS_MANA_DEATH')):
                                               return True
    
               elif pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_BOUND_BY_COMPACT')):
                   if gc.getTerrainInfo(pPlot.getTerrainType()).getTerrainDown() == -1:#i.e., is not Hell terrain
                           return True
    
           if pPlot.isPythonActive():
               if pUnit.isAutomated() or not pUnit.isHuman():
                   if pPlot.getFeatureType() == gc.getInfoTypeForString('FEATURE_HALLOWED_GROUND'):
                       if pUnit.getRace() in [gc.getInfoTypeForString('PROMOTION_DEMON'),gc.getInfoTypeForString('PROMOTION_UNDEAD')]:
                           return False
                   iImprovement = pPlot.getImprovementType()
                   if iImprovement == gc.getInfoTypeForString('IMPROVEMENT_TARCHS_TOWER'):
                       if not pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_CHANNELING2')):
                           return True
                   elif iImprovement == gc.getInfoTypeForString('IMPROVEMENT_MAELSTROM'):
                       if not pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_AFFINITY_AIR')):
                           return True
                   elif iImprovement == gc.getInfoTypeForString('IMPROVEMENT_MIRROR_OF_HEAVEN'):
                       if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_VAMPIRE')):
                           return True
                       elif pUnit.getUnitClassType() == gc.getInfoTypeForString('UNITCLASS_SHADOWRIDER'):
                           return True
                   elif iImprovement == gc.getInfoTypeForString('IMPROVEMENT_WHISPERING_WOOD'):
                       if pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_DEASIL_CHARM')):
                           pass
                       elif pUnit.isHasPromotion(gc.getInfoTypeForString('PROMOTION_SHADOWWALK')):
                           pass
                       elif pUnit.getUnitClassType() == gc.getInfoTypeForString('UNITCLASS_AURIC'):
                           pass
                       else:
                           return True
           return False
    Of course, if you wanted to make the city impassible to all lower level units, including Runewyns and friendly forces, then it would be simpler and more efficient to use pPlot.setMinLevel(x) on the city tile. Unfortunately def onBuildingBuilt(self, argsList): does not get triggered when a building is added by a spell, and there is no such call as def onBuildingRemoved(self, argsList):. Every city is already checked every turn though, so it would be easy to check to see whether a city has a Greater Ring of Warding and to adjust the minimum level of the city's plot accordingly.
     
    Last edited: Feb 14, 2019
  6. Calavente

    Calavente Richard's voice

    Joined:
    Jun 4, 2006
    Messages:
    2,728
    Location:
    France
    woooah !
    many thanks.
    I was really only wondering if it could work for a city (using a temporary building for example).. I didn't expect any real code.

    and I have to hang myself for being the cause of you reporting the upload ....

    anyway, my idea was mostly theoretical. And game-wise, I thought that being able to use an archmage to forbid access to low-level units could be interesting enough, even if the level is low (2-3 max) (which is better so that it doesn"t become OP), and even if your units are blocked (magic, especially Force, is neutral).
    However if you want to give access to your units, it's easy, move the Archmage, wait a turn,... and hop, it's good.
    I didn't think about Runwynes; but for an easy way to offset that, Runewynes could come with free xp, even if it's not the smartest solution.
     
  7. MagisterCultuum

    MagisterCultuum Great Sage

    Joined:
    Feb 14, 2007
    Messages:
    15,860
    Location:
    Kael's head
    You could give Runewyns free xp, but unless they also start with free promotion picks they would not be able to use them on to level up on the turn they are summoned. Most summon never live long enough to use the xp they gain, unless they are permanent summons or perhaps belong to a player with a summoner trait. Giving Runewyns free xp would only help Basium, Keelyn, Tya, Rivanna, Hyborem, or the Sheaim. It would also have to be a lot of xp, giving them a lot of promotions and passing on a lot of xp to a summoner with Dimensional 1/2/Affinity. Ceridwen is the closest Dagda has to a true enemy, so that is very anti-thematic.

    It would make much more sense to just increase the level directly, without granting any xp. Since the Mercurians cannot upgrade an Angel to a Runewyn until level 7 anyway, making Runewyns always start at level 7 seems quite reasonable. That only requires adding these two lines within def onUnitCreated(self, argsList):
    Code:
           if iUnit == gc.getInfoTypeForString('UNIT_RUNEWYN'):
               unit.setLevel(7)
    
    Runewyns would still benefit from free promotion picks based on the Force mana supply if you have a Tower of Divination, but it would be practically impossible for Runewyn summoned by a summer trait player to ever earn enough xp to level the unit up like they could most other summons. I am fine with that.


    I have also considered letting Dispel Magic remove spell buildings (and improvements, if I decided to have the Greater Ring of Warding implemented that way) from adjacent cities. It makes sense that it if can remove buff promotions from spells it could remove buildings/improvements from spells too. I used to have Runewyns start with Metamagic 2 so they could cast Dispel Magic, and could give it back. I think I only removed it because the Crown of Akharien used to grant Channeling 3 to summons and I did not want any Runewyns summoning Djinns.


    Of course, if you just use the def unitCannotMoveInto(self,argsList): code I posted this won't matter, as that already makes an exception for Magic Immune units.
     
    Nothrazim likes this.
  8. MagisterCultuum

    MagisterCultuum Great Sage

    Joined:
    Feb 14, 2007
    Messages:
    15,860
    Location:
    Kael's head
    What would you think if I gave Force Affinity a Mobile Ring of Warding spell, where the Ring of Warding improvement itself and the minimum level requirement to enter the tile moves along with the caster?

    That is how I have it working now.

    I have not completely worked out yet the best thing to do in cases where multiple Force Affinity casters are on the same tile and do not move together. Currently the spell will not work on tiles adjacent to other Rings of Warding. It removes the Ring from a tile when no more Force Affinity casters remain on the tile, but if multiple casters are on the tile then one caster moving would not dispel the old one and thus the new tile would not be eligible for a new Ring. Would it be better to make the ring follow the caster with the highest level or larger effective supply of Force mana instead?

    I originally made the Ring of Warding improvement a permanent improvement, and added another spell to remove it. Then realized that was completely unnecessary. If I do not give the improvement the bPermanent tag then it may be pillaged by any unit on the tile (including the caster himself) in order to dispel it. A few lines of Python in CvEventManager.py can make the plot revert to its previous improvement type and the minimum level revert to 0 no matter how it is removed.

    The game does not let units pillage improvements on city tiles, so I cannot make the improvement version of ring of warding work within cities. Leaving the old Greater Ring of Warding as well as the improvement version thus makes sense.

    One potential problem with using the temporary improvement mechanic is that I don't think there is a way to save the progress of the old/real improvement upgrading. Casting the spell on a Village that was ready to upgrade to a town the following turn, or a Castle that was ready to upgrade to a Citadel, would remove that progress.


    As it currently works, the spell can temporarily remove access to bonuses on tiles that are connected by other improvements. I have not decided yet whether I want to make the improvement itself able to connect resources.

    I am pretty sure it should be blocked on Unique Features, so as to prevent you from using it to stop rivals from exploring great lairs or to make Letum Frigus disappear just before Auric is summoned there.

    In order to make the improvement one that is maintained by a caster I had to make the spell give the improvement a temporary timer of 2 and the Force Affinity promotion a PyPerTurn effect to increase the timer back to 2 whenever it gets lower than 2. I first tried a timer of 1, but then it would expire before the PyPerTurn effect could trigger to update it. I still have the PythonAtRange effect remove it if a caster is not on the tile.

    I had originally made a Runewyn remove the improvement in adjacent tiles through the same call, but I'm thinking of changing that. With Runewyns starting at level 7 they can already pass through most Rings of Warding. They already remove the improvement spell effect in their post combat python call, and I gave them Metamagic 2 so they could dispel the rings when you want it anyway.

    In addition to making the plot impassible to low level units, the Ring of Warding improvement also functions like a fort increasing the defense of that and adjacent tiles. I do not currently have it act as a superfort, extending the player's culture, but I suppose I could.

    Do you think the Ring of Warding improvement spell should be limited to being used only within the caster's players or team's cultural borders, or should it be available anywhere (except cities or unique features)?
     

Share This Page