Wiki

Clone wiki

ExtraModMod / ProposalSpellSystem

#More Naval AI - Spell system changes

The major point of this proposal is to rewrite the description of spells in a way that allows to check what game entities would be affected by a certain spell, and in which way they would be affected, in a simpler way. It splits the currently monolithic spells into different parts which can be taken into account separatedly:

  • Spell description: Name, description, button and other informational tags.
  • Caster prerequisites: Prerequisites that the unit which wants to cast the spell and its owner must meet in order to be able to cast it.
  • Affected area: Allows to define the area in which the spell will take place. Entities in that area may be affected by the spell's effects. If a spell has no area it will only be able to use certain types of spell effects.
  • Spell effects: List of different effects that the spell will have. Each effect is evaluated in order, and each one of them has clear prerequisites, affected entities and a description of how they will be affected. If a spell cannot apply at least one of its effects, it will fail. It will be possible to define how to apply these effects (all of them, first one, optional ones and so on).

Thanks to this separation, it could be possible to evaluate how a certain spell would affect a player, city, terrain tile or unit in order to let the AI calculate if it wants to do it or not. For example, if the AI knows that a specific spell only affects tiles, it could be taught to send units with this kind of spell on "improve tiles of my empire" missions which would evaluate each tile of the empire against the spell in order to know if it is beneficial to apply it or not, without needing any hardcoded code.

It will also be possible for the AI to know if it will be able to cast a certain spell (by getting a certain unit with specific promotions, and so on) to have the required information to make its plans. The AI will also be able to know what tiles will be affected by a certain spell.

I have taken special care with this proposal in allowing to define damaging spells and terraforming spells in a simpler way (as those are the ones more important for the AI, IMO). All of these changes are coded in a way which simplifies how to code spells, and as a result it would be possible to turn many spells currently hardcoded in python into XML only spells.

These changes imply rewriting XML spell descriptions, merging Python effects into XML descriptions when possible and changing both the DLL code that checks prerrequisites and the DLL code that applies spells. If it is considered that this proposal would not help the AI, it would IMO not be worth the effort of implementing it no matter how many other goodies it brings as a side effect.

I would also like to mention that this is in no way a proposal thought for the short term. I would code the XML schema changes, the DLL changes related to it and the Python changes resulting of them, and I would also try to help with the AI changes that would follow. I think that only the new system mentioned here (without any AI improvements) would take months to complete and of course it would require a lot of testing.Therefore, it could also be considered that this proposal is going to be too time consuming for what it does even if it manages to reach its goals.

I have not made a detailed design of the DLL additions and changes for now; I'll wait until I see what reception this proposal gets. My idea is to resort the calculation of prerrequisites to enforce that caster prerrequisites are calculated first, then the affected area and then the game would check if the spell has any effect. In each category, prerrequisites would be also sorted in order to evaluate first the ones which are simpler to calculate or prevent doing a lot more calculations because they discard a lot of possible casters.

##SpellInfo XML schema changes

A complete description of the suggested XML schema for spells (including both reused tags from the existing implementation and new ones) can be found below. Removed tags are not mentioned.

###Spell description:

  • Type: Type of the spell.

  • Description: TXT string to use for the description of this spell.

  • Civilopedia: TXT string to use for the flavour text of this spell.

  • Strategy: TXT string to use for the strategy text of this spell.

  • Help: TXT string to use for the flavour text of this spell.

  • PyHelp: Optional tag which defines a custom help text for this spell. Patch provided by lfgr, see http://sourceforge.net/p/tholalsffhaimod/patches/6/

  • Effect: Special effect to use for this spell.

  • Sound: Sound to use for this spell.

  • Button: Button icon to use for this spell.

  • bAllowAutomateTerrain: Optional tag (will assume 0 if not present). When set to 1, it indicates that it is possible for a human player to automate the casting of this spell. Having this tag set to 1 when the spell has prerequisites which refer to entities other than the caster, the caster's owner or the tile in which the spell is being cast will throw an assertion when in debug mode. The assertion will also be thrown if the spell affects something that is not a tile.

  • iAIWeight: Optional tag (will assume 0 if not present). Additional weight for the AI to use this spell. This proposal will try to reduce the usage of this tag as much as possible.

  • bDisplayWhenDisabled: Optional tag (will assume 0 if not present). When set to 1, this spell will be shown in the GUI if the unit fulfills its prerequisites even when it cannot be used.

  • bHasCasted: Optional tag (will assume 1 if not present). When set to 0, this spell will not mark the unit as if it already has casted a spell this turn after being used.

  • iResistModify: Optional tag (will assume 0 if not present). This number modifies spell resistance for all effects with bResistable set to 1.

  • bAbility: Optional tag (will assume 0 if not present). When set to 1, it indicates that this is not an spell but an ability.

  • iDelay: Optional tag (will assume 0 if not present). Indicates the number of turns required for casting this spell.

  • bGraphicalOnly: Optional tag (will assume 0 if not present). This spell will not appear in the Civilopedia.

###Caster prerequisites:

All prerequisite sections are optional. For boolean and numeric sections, their default value is indicated. For all other sections, if they are not present then their conditions are not taken into account in order to calculate prerequisites for this spell.

  • PromotionAndPrereqs: Section with the list of promotions that a unit must have in order to be able to cast this spell. This section may have any number of Promotion sections. It is not possible to use

  • PromotionAndBlocks: Section with the list of promotions that a unit must not have in order to be able to cast this spell. This section may have any number of Promotion sections.

  • UnitOrPrereqs: Section with a list of unit types. The caster must be of one of them in order to cast the spell. This section may have any number of Unit sections.

  • UnitClassOrPrereqs: Section with a list of unit classes. The caster must have one of them in order to cast the spell. This section may have any number of UnitClass sections.

  • UnitCombatOrPrereqs: Section with a list of unit combats. The caster must have one of them in order to cast the spell. This section may have any number of UnitCombat sections. It is not possible to use both UnitCombatOrPrereqs and UnitCombatsAndBlocks at the same time.

  • UnitCombatsAndBlocks: Section with a list of unit combats. The caster must not have any of them in order to cast the spell. This section may have any number of UnitCombat sections. It is not possible to use both UnitCombatOrPrereqs and UnitCombatsAndBlocks at the same time.

  • BuildingClassOwnedPrereq: The owner of the unit must have at least one building of this BuildingClass in order to cast the spell.

  • CivilizationPrereq: Only this civilization can cast this spell.

  • ReligionPrereq: The unit must follow this religion in order to cast the spell.

  • StateReligionPrereq: The caster's owner must have this state religion in order to cast the spell.

  • TechPrereq: This technology must have been researched by the caster's owner in order to cast the spell.

  • bAllowAI: Optional tag (will assume 1 if not present). When set to 0, AI players cannot cast the spell.

  • bAllowAuto: Unused tag. I wonder why it was added.

  • bCasterMustBeAlive: Optional tag (will assume 0 if not present). When set to 1, this spell can only be cast by living units.

  • bCasterNoDuration: Optional tag (will assume 0 if not present). When set to 1, this spell cannot be cast by temporary units. Temporary units are usually summons.

  • bGlobal: Optional tag (will assume 0 if not present). If set to 1, it indicates that this is a World Spell and as a result it can only be cast if they are not disabled by GameOption and the player still has not casted it.

  • VotePrereq: The caster's owner must be a full member of a council that has passed this vote.

  • iCasterMinLevel: Optional tag (will assume 0 if not present). Minimum level that the caster needs in order to cast this spell.

  • bIgnoreHasCasted: Optional tag (will assume 0 if not present). When set to 1, this spell can be casted even if the unit already has casted this turn.

  • bNoSpellUnit: Spell units cannot cast this spell.

  • bNoIllusionary: Illusions cannot cast this spell.

  • iCost: Optional tag (will assume 0 if not present). The caster's owner must have this amount of gold available in order to cast the spell. This cost will also be taken from the owner's treasury when the spell is cast.

  • PyRequirement: Optional tag which defines a python callback that will be checked to know if the spell can be casted. The callback receives as parameter the unit which casts the spell and returns either True or False. Unless it is being used to enforce certain specifical conditions for the AI to use this spell, this callback should be used as sparingly as possible. The reason is that the AI will not be able to understand when to cast a spell if the PyRequirement is present.

###Affected area:

Each spell must define the tile or tiles it will affect. A spell can only be cast if its target tile calculation returns at least one tile. This is made in the sections described here. These tags are defined directly in the <SpellInfo> tag.

  • TargetType: Defines how the tiles that will affect the spell are decided. The TargetType section is optional. When not present, the game will assume SPELLTARGET_RADIUS. A description of different TargetTypes can be found below.

    • SPELLTARGET_RADIUS: This spell will affect all tiles surrounding the caster (including the caster's tile), in a radius defined by the iRange section.

    • SPELLTARGET_RING: This spell will affect all tiles surrounding the caster (excluding the caster's tile), in a radius defined by the iRange section.

    • SPELLTARGET_WORLD: All tiles in the world are affected by this spell. iRange will be ignored.

    • SPELLTARGET_PYTHON: The target tiles are provided by a python callback.

    • SPELLTARGET_NONE: This spell does not affect tiles. Only spell effects with ENTITY_GLOBAL can be used.

  • iRange: Optional tag (will assume 1 if not present). Indicates the tile range used by the TargetType. Even when using SPELLTARGET_PYTHON, using iRange to define how the custom affected area works is a good practice as the AI could use iRange to judge how useful the area is.

  • PyTargetTiles: Optional tag which is only taken into account when TargetType is set to SPELLTARGET_PYTHON. Python callback used to determine the tiles targeted by this spell. This callback will receive as a parameter the unit casting the spell and the iRange tag. It will return a list of tiles. Although the AI will not be able to know how those tiles are decided (and therefore it won't be able to know where to place the caster and so on), it will still be able to evaluate how the spell will affect entities which are present in those tiles.

###Spell effects:

Each spell will trigger one or more effects over game entities which are present in the tiles they target. Each effect may define additional requirements for itself in order to work against a given target. In order to allow casting a spell, it must affect at least one entity with one effect. Spell effects are defined in a SpellEffects section, which may have up to NUM_SPELL_EFFECTS SpellEffect sections, where NUM_SPELL_EFFECTS is a new global constant defined in GlobalDefinesAlt.

Unless otherwise noted, the game will try to apply ALL spell effects in the order they have been defined to all entities that meet each spell effect prerequisites. If one of these effects fails to affect at least one entity, it will not be possible to cast the spell. Spell effects that only define prerequisites are an exception; they will succeed as long as there is at least one entity that fulfills their requisites. It is possible to modify these rules by using certain spell effect tags.

  • TargetEntity: Indicates the entity that is targeted by this SpellEffect. Most of the tags that can be used in the SpellEffect section require a specific TargetEntity. Using a SpellEffect tag for the wrong TargetEntity will cause an assertion, and the tag will be ignored by the game. The tags specific to each TargetEntity are described later on. The different TargetEntities are:

  • ENTITY_GLOBAL: This SpellEffect only changes global or player values.

  • ENTITY_UNIT: This SpellEffect targets units.

  • ENTITY_TILE: This SpellEffect targets tiles.

  • ENTITY_CITY: This SpellEffect targets cities.

The following tags are common to all spell effects, regardless of their TargetEntity.

  • bImmunePlayer: When set to 1, entities owned by the caster's civilization are immune to this effect.

  • bImmuneTeam: When set to 1, entities owned by the caster's team are immune to this effect (but the caster's civilization isn't).

  • bImmuneNeutral: When set to 1, entities owned by teams that are not at war with the caster's team are immune to this effect.

  • bImmuneEnemy: When set to 1, entities owned by teams that are at war with the caster's team are immune to this effect.

  • iSuccessChance: Optional tag (will assume 100 if not present). Indicates the % chance of success of this effect. If a spell does not get the required success chance when it is cast, it is not considered as if the spell effect has failed.

  • bAdjacentToWater: Cannot be used with ENTITY_GLOBAL. Optional tag (will assume 0 if not present). When set to 1, the spell effect will ignore entities which are not in tiles adjacent to water.

  • PlotTypeOrPrereqs: Cannot be used with ENTITY_GLOBAL. Section with a list of plot types. Entities which are not in a tile with one of these plot types will not be affected by the spell. This section may have any number of Plot sections. It is not possible to use both PlotTypeOrPrereqs and PlotTypeAndBlocks at the same time.

  • PlotTypeAndBlocks: Cannot be used with ENTITY_GLOBAL. Section with a list of plot types. Entities which are in a tile with one of these plot types will not be affected by the spell. This section may have any number of Plot sections.

  • bRiver: Cannot be used with ENTITY_GLOBAL. Optional tag (will assume 0 if not present). When set to 1, the spell effect will ignore entities which are not in tiles with a river.

  • TerrainOrPrereqs: Cannot be used with ENTITY_GLOBAL. Section with a list of terrain types. Entities which are not in a tile with one of these terrain types will not be affected by the spell. This section may have any number of Terrain sections.

  • FeatureOrPrereqs: Cannot be used with ENTITY_GLOBAL. Section with a list of features. Entities which are not in a tile with one of these feature types will not be affected by the spell. This section may have any number of Feature sections.

  • FeatureAndBlocks: Cannot be used with ENTITY_GLOBAL. Section with a list of features. Entities which are in a tile with one of these terrain types will not be affected by the spell. This section may have any number of Feature sections. It is not possible to use both FeatureOrPrereqs and FeatureAndBlocksat the same time.

  • bFlammableFeature: When set to 1, this effect requires a flammable feature in the tile in order to affect entities in it.

  • ImprovementOrPrereqs: Cannot be used with ENTITY_GLOBAL. Section with a list of improvements. Entities which are not in a tile with one of these improvement types will not be affected by the spell. This section may have any number of improvement sections.

  • BonusOrPrereqs: Cannot be used with ENTITY_GLOBAL. Section with a list of bonuses. Entities which are not in a tile with one of these bonus types will not be affected by the spell. This section may have any number of Bonus sections.

  • BuildingOrPrereqs: Cannot be used with ENTITY_GLOBAL. Section with a list of buildings. Entities which are not in a city tile with one of these building types will not be affected by the spell. This section may have any number of Building sections. Using this tag forces the effect to only take place in tiles with cities.

  • bNotRequired: When set to 1, the game will not enforce the success of this effect in order to allow casting the spell. If all spell effects have bNotRequired set to true, the game will still require at least one of them to succeed. This tag is usually used either for effects which may happen or not (such as the smoke creation of Pillar of Fire) or for spells which have a group of different effects depending on mutually exclusive conditions (like Vitalize).

  • bCausesWar: Optional tag (will assume 0 if not present). When set to 1, it indicates that if this spell effect succeeds in affecting the territory or units of other civilizations, it will trigger a war.

Tags which are entity specific are described below. Some of them may define additional requisites.

####ENTITY_GLOBAL:

  • iChangeDisableProduction: Changes the disable production counter of all affected players by the specified value. The value will be converted to the current game speed by using ((iChangeDisableProduction * gameSpeedVictoryDelayPercent) / 100) first.

  • iChangeDisableResearch: Changes the disable research counter of all affected players by the specified value. The value will be converted to the current game speed by using ((iChangeDisableResearch * gameSpeedVictoryDelayPercent) / 100) first.

####ENTITY_UNIT:

Spell effects for units are special because they can sometimes be resisted (see bResistable and iResistModify). Before applying spell effects, the game will already have a list of units against which it will apply the effects of the spell. In the case that a spell has resistable effects, the game will check first for each unit if it resists the spell. If a unit resists the spell, all unit spell effects with bResistable set to 1 will not be applied to it. Unit spell effects without bResistable will apply to units which have resisted the spell normally. As a result of spell resistance, it is possible that one of the spell effects will fail to affect any units. This is not considered as if the spell effect has failed.

  • bResistable: Optional tag (will assume 0 if not present). When set to 1, this effect can be resisted by units. Resisting an effect means that none of its effects will affect the unit that resisted it. An spell effect could cause war declarations (see bCausesWar) even if it is resisted.

  • bCasterOnly: When set to 1, this SpellEffect will only affect the caster.

  • bImmuneFlying: When set to 1, flying units are immune to this effect.

  • bImmuneNotAlive: When set to 1, units that are not alive are immune to this effect.

  • bImmuneAlive: When set to 1, units that are alive are immune to this effect.

  • UnitOrPrereqs: Section with a list of unit types. Only units with these types will be affected. This section may have any number of Unit sections.

  • UnitClassOrPrereqs: Section with a list of unit classes. Only units with these unit classes will be affected. This section may have any number of UnitClass sections.

  • UnitCombatOrPrereqs: Section with a list of unit combats. Only units with these unit combats will be affected. This section may have any number of UnitCombat sections.

  • PromotionAndPrereqs: Section with the list of promotions. Only units with all of these promotions will be affected. This section may have any number of Promotion sections.

  • PromotionAndBlocks: Section with the list of promotions. Only units with none of these promotions will be affected. This section may have any number of Promotion sections.

  • DamageType: Damage type caused by this SpellEffect. Units immune to this damage type will not be considered as affected by any part of this spell effect. Thanks to this, it will be possible to, for example, apply promotions only to units which are not immune to a certain type of damage. If this behavior is not desired, it is always possible to create separate spell effects.

  • iDamage: Optional tag (will assume 0 if not present). ToDo: description (check how spell damage works).

  • iDamageLimit: Optional tag (will assume 100 if not present). ToDo: description (check how spell damage limits work).

  • AddPromotion: The spell effect will try to add this promotion to the affected units. If the unit already has this promotion, the unit will be considered as not affected by this part of the spell effect.

  • bEnforceAddPromotionPrereqs: AddPromotion must take into account the promotion prerequisites in order to allow adding it.

  • RemovePromotion: The spell effect will try to remove this promotion to the affected units. If it is not possible to remove this promotion from the unit, the unit will be considered as not affected by this part of the spell effect.

  • bDispel: This effect will try to remove all dispellable promotions from affected units.

  • ConvertUnitType: Unit type to which affected units will be converted. National Unit limits will be enforced (it is not possible to get National Units beyond the limit by using this). If a unit is converted into a World Unit, the game will mark that unit as already built. If a World Unit is converted into another kind of unit, that type of World Unit will still count as built.

  • bSetHasCasted: If this tag is set to 0, affected units will be able to cast spells again. If set to 1, affected units will be set as if they already had casted an spell this turn.

  • bKill: If this tag is set to 1, affected units will die.

  • iImmobileTurns: Indicates the number of turns that affected units will stay immobile.

  • PyUnitEffect: Optional tag which defines a python callback that will be applied to all units affected by this SpellEffect. The callback receives as parameter the unit which casts the spell and a list of units affected by it (with units that not meet additional effect prerequisites such as bImmuneEnemy or bImmuneNotAlive already removed from the list). When PyUnitEffect is used, the effect is always considered to have affected something (unless the prerrequisites of the ENTITY_UNIT SpellEffect it has been defined in are not met). This callback should be used as sparingly as possible, since the AI will not be able to understand what it does.

####ENTITY_TILE:

  • ConvertPlot: The plot type of this tile will be set to this type.

  • ConvertTerrain: The terrain of this tile will be set to this type.

  • ConvertFeature: The feature of this tile will be set to this type.

  • ConvertImprovement: The improvement of this tile will be set to this type. ConvertImprovement can create Unique Features as long as they don't exist in the map already, but it will not allow to create duplicates. It cannot remove Unique Features.

  • bImprovementUnique: Allows ConvertImprovement to create Unique Features even if one of the same type already exists, or to remove Unique Features from the map.

  • ConvertBonus: The bonus of this tile will be set to this type.

  • CreateUnitType: Units of this type will be created in this tile. National Unit limits will be enforced (it is not possible to get National Units beyond the limit by using this). If a World Unit is created, the game will mark that unit as already built.

  • iCreateUnitNum: Number of CreateUnitType units to create in this tile.

  • bCopyCastersPromotions: Units created by this effect will copy the caster's promotions.

  • iUnitDuration: Optional tag. If not present it will have a default value of 2. Determines the duration of summoned units. It will apply any modifications to summon duration on top of this value. Setting it to -1 creates permanent units instead of temporary ones.

  • CreateUnitPromotion: Grants this promotion to created units.

  • PyTileEffect: Optional tag which defines a python callback that will be applied to all tiles affected by the spell. The callback receives as parameter the unit which casts the spell and a list of tiles affected by it (with tiles that do not meet additional effect prerequisites such as bImmuneEnemy or bImmuneNotAlive already removed from the list). When PyTileEffect is used, the effect is always considered to have affected something (unless the prerrequisites of the ENTITY_TILE SpellEffect it has been defined in are not met). This callback should be used as sparingly as possible, since the AI will not be able to understand what it does.

####ENTITY_CITY:

  • bNoCapital: When set to 1, capital cities are immune to this effect.

  • bCapital: When set to 1, only capital cities are affected.

  • CreateBuildingType: This building type will be created in the affected cities. If the cities already have this building type they will be considered as not affected by this part of the spell effect. This effect will not be able to create unique buildings (world wonders, team wonders and national wonders) if creating such a building would break the rules of each different unique building type (no more than 1 world wonder and so on).

  • bCreateWorldWonder: When set to 1, it allows CreateBuildingType to create a world wonder even if it is marked as "already built by someone", as long as it is not currently built anywhere else in the world.

  • RemoveBuildingType: This building type will be removed from the affected cities. If the cities do not have this building type they will be considered as not affected by this part of the spell effect. If this effect removes a world wonder, it will still be marked as "already built by someone".

  • SpreadReligion: The specified religion will be spread to the affected cities.

  • iChangePopulation: The affected cities' population will change by this value. It is not possible to set the population of a city below 0 with this effect. With negative values and a population of 1, the effect will fail against the city.

  • PyCityEffect: Optional tag which defines a python callback that will be applied to all cities affected by the spell. The callback receives as parameter the unit which casts the spell and a list of cities affected by it (with cities that not meet additional effect prerequisites such as bImmuneEnemy or bImmuneTeam already removed from the list). When PyCityEffect is used, the effect is always considered to have affected something (unless the prerrequisites of the ENTITY_CITY SpellEffect it has been defined in are not met). This callback should be used as sparingly as possible, since the AI will not be able to understand what it does.

##SpellInfo XML schema examples

###Global spell

#!xml
<SpellInfo>
    <Type>SPELL_STASIS</Type>
    <Description>TXT_KEY_SPELL_STASIS</Description>
    <Civilopedia>TXT_KEY_SPELL_PLACEHOLDER_PEDIA</Civilopedia>
    <Help>TXT_KEY_SPELL_STASIS_HELP</Help>
    <CivilizationPrereq>CIVILIZATION_ILLIANS</CivilizationPrereq>
    <bGlobal>1</bGlobal>
    <bDisplayWhenDisabled>1</bDisplayWhenDisabled>
    <TargetType>SPELLTARGET_NONE</TargetType>
    <SpellEffects>
        <SpellEffect>
            <TargetEntity>ENTITY_GLOBAL</TargetEntity>
            <bImmuneTeam>1</bImmuneTeam>
            <iChangeDisableProduction>20</iChangeDisableProduction>
            <iChangeDisableResearch>20</iChangeDisableResearch>
        </SpellEffect>
    </SpellEffects>
    <Effect>EFFECT_SPELL1</Effect>
    <Sound>AS3D_SPELL_HASTE</Sound>
    <Button>Art/Interface/Buttons/Spells/Stasis.dds</Button>
</SpellInfo>

###Spell which targets all cities in the game

#!xml
<SpellInfo>
    <Type>SPELL_RIVER_OF_BLOOD</Type>
    <Description>TXT_KEY_SPELL_RIVER_OF_BLOOD</Description>
    <Civilopedia>TXT_KEY_SPELL_PLACEHOLDER_PEDIA</Civilopedia>
    <Help>TXT_KEY_SPELL_RIVER_OF_BLOOD_HELP</Help>
    <CivilizationPrereq>CIVILIZATION_CALABIM</CivilizationPrereq>
    <bGlobal>1</bGlobal>
    <bDisplayWhenDisabled>1</bDisplayWhenDisabled>
    <!-- The requirement only contains the AI logic now. -->
    <PyRequirement>reqRiverOfBlood(pCaster)</PyRequirement>
    <TargetType>SPELLTARGET_WORLD</TargetType>
    <SpellEffects>
        <SpellEffect>
            <TargetEntity>ENTITY_CITY</TargetEntity>
            <bImmunePlayer>1</bImmunePlayer>
            <iChangePopulation>-2<iChangePopulation>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_CITY</TargetEntity>
            <bImmuneTeam>1</bImmuneTeam>
            <bImmuneNeutral>1</bImmuneNeutral>
            <bImmuneEnemy>1</bImmuneEnemy>
            <iChangePopulation>2<iChangePopulation>
        </SpellEffect>
    </SpellEffects>
    <Effect>EFFECT_SPELL1</Effect>
    <Sound>AS3D_SPELL_INSPIRATION</Sound>
    <Button>Art/Interface/Buttons/Spells/River of Blood.dds</Button>
</SpellInfo>

###Take equipment from city

#!xml
<SpellInfo>
    <Type>SPELL_TAKE_CROWN_OF_AKHARIEN_BUILDING</Type>
    <Description>TXT_KEY_SPELL_TAKE_CROWN_OF_AKHARIEN</Description>
    <Civilopedia>TXT_KEY_SPELL_PLACEHOLDER_PEDIA</Civilopedia>
    <Help>TXT_KEY_SPELL_TAKE_EQUIPMENT_HELP</Help>
    <bHasCasted>1</bHasCasted>
    <bAbility>1</bAbility>
    <PromotionAndBlocks>
        <Promotion>PROMOTION_CROWN_OF_AKHARIEN</Promotion>
    </PromotionAndBlocks>
    <UnitCombatsAndBlocks>
        <UnitCombat>UNITCOMBAT_NAVAL</UnitCombat>
        <UnitCombat>UNITCOMBAT_SIEGE</UnitCombat>
    </UnitCombatsAndBlocks>
    <bNoSpellUnit>1</bNoSpellUnit>
    <bNoIllusion>1</bNoIllusion>
    <SpellEffects>
        <!--
            Since the spell area is the same tile in which the caster is at the moment,
            removing the crown from the city first forces this spell to be cast in cities only.
            If the crown is not present, the unit will not be able to take it.
        -->
        <SpellEffect>
            <TargetEntity>ENTITY_CITY</TargetEntity>
            <bImmuneTeam>1</bImmuneTeam>
            <bImmuneNeutral>1</bImmuneNeutral>
            <bImmuneEnemy>1</bImmuneEnemy>
            <BuildingOrPrereqs>
                <Building>BUILDING_CROWN_OF_AKHARIEN</Building>
            <BuildingOrPrereqs>
            <RemoveBuildingType>BUILDING_CROWN_OF_AKHARIEN</RemoveBuildingType>
        </SpellEffect>
        <!-- Removing the crown from the city first forces this spell to be cast in cities only. -->
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <bCasterOnly>1</bCasterOnly>
            <AddPromotion>PROMOTION_CROWN_OF_AKHARIEN</AddPromotions>
        </SpellEffect>
    <SpellEffects>
    <Effect>EFFECT_SPELL1</Effect>
    <Sound>AS3D_SPELL_TRAIN</Sound>
    <bGraphicalOnly>1</bGraphicalOnly>
    <Button>Art/Interface/Buttons/Equipment/CrownOfAkharien.dds</Button>
</SpellInfo>

###Take equipment from unit

#!xml
<SpellInfo>
    <Type>SPELL_TAKE_CROWN_OF_AKHARIEN_PROMOTION</Type>
    <Description>TXT_KEY_SPELL_TAKE_CROWN_OF_AKHARIEN</Description>
    <Civilopedia>TXT_KEY_SPELL_PLACEHOLDER_PEDIA</Civilopedia>
    <Help>TXT_KEY_SPELL_TAKE_EQUIPMENT_HELP</Help>
    <bAllowAI>1</bAllowAI>
    <bHasCasted>1</bHasCasted>
    <bAbility>1</bAbility>
    <SpellEffects>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <bImmuneTeam>1</bImmuneTeam>
            <bImmuneNeutral>1</bImmuneNeutral>
            <bImmuneEnemy>1</bImmuneEnemy>
            <PromotionAndPrereqs>
                <Promotion>PROMOTION_CROWN_OF_AKHARIEN</Promotion>
            </PromotionAndPrereqs>
            <RemovePromotion>
                <Promotion>PROMOTION_CROWN_OF_AKHARIEN</Promotion>
            </RemovePromotion>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <bCasterOnly>1</bCasterOnly>
            <AddPromotion>PROMOTION_CROWN_OF_AKHARIEN</AddPromotions>
        </SpellEffect>
    </SpellEffects>
    <Effect>EFFECT_SPELL1</Effect>
    <Sound>AS3D_SPELL_TRAIN</Sound>
    <bGraphicalOnly>1</bGraphicalOnly>
    <Button>Art/Interface/Buttons/Equipment/CrownOfAkharien.dds</Button>
</SpellInfo>

###Drop equipment in city

#!xml
<SpellInfo>
    <Type>SPELL_DROP_CROWN_OF_AKHARIEN</Type>
    <Description>TXT_KEY_SPELL_DROP_CROWN_OF_AKHARIEN</Description>
    <Civilopedia>TXT_KEY_SPELL_PLACEHOLDER_PEDIA</Civilopedia>
    <PromotionPrereq1>PROMOTION_CROWN_OF_AKHARIEN</PromotionPrereq1>
    <bAllowAI>1</bAllowAI>
    <bInBordersOnly>1</bInBordersOnly>
    <bInCityOnly>1</bInCityOnly>
    <bDisplayWhenDisabled>1</bDisplayWhenDisabled>
    <bHasCasted>1</bHasCasted>
    <bAbility>1</bAbility>
    <SpellEffects>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <bCasterOnly>1</bCasterOnly>
            <PromotionAndPrereqs>
                <Promotion>PROMOTION_CROWN_OF_AKHARIEN</Promotion>
            </PromotionAndPrereqs>
            <RemovePromotion>
                <Promotion>PROMOTION_CROWN_OF_AKHARIEN</Promotion>
            </RemovePromotion>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_CITY</TargetEntity>
            <bImmuneTeam>1</bImmuneTeam>
            <bImmuneNeutral>1</bImmuneNeutral>
            <bImmuneEnemy>1</bImmuneEnemy>
            <CreateBuildingType>BUILDING_CROWN_OF_AKHARIEN</CreateBuildingType>
            <bCreateWorldWonder>1</bCreateWorldWonder>
        </SpellEffect>
    </SpellEffects>
    <Effect>EFFECT_SPELL1</Effect>
    <Sound>AS3D_SPELL_TRAIN</Sound>
    <bGraphicalOnly>1</bGraphicalOnly>
    <Button>Art/Interface/Buttons/Equipment/CrownOfAkharien.dds</Button>
</SpellInfo>

###Damaging spell that may also destroy improvements

Tsunami also demonstrates how to use spell effects in order to set complex requirements.

#!xml
<SpellInfo>
    <Type>SPELL_TSUNAMI</Type>
    <Description>TXT_KEY_SPELL_TSUNAMI</Description>
    <Civilopedia>TXT_KEY_SPELL_PLACEHOLDER_PEDIA</Civilopedia>
    <Help>TXT_KEY_SPELL_TSUNAMI_HELP</Help>
    <PromotionPrereq1>PROMOTION_CHANNELING2</PromotionPrereq1>
    <PromotionPrereq2>PROMOTION_DIVINE</PromotionPrereq2>
    <ReligionPrereq>RELIGION_OCTOPUS_OVERLORDS</ReligionPrereq>
    <bDisplayWhenDisabled>1</bDisplayWhenDisabled>
    <bHasCasted>1</bHasCasted>
    <TargetType>SPELLTARGET_RING</TargetType>
    <iRange>2</iRange>
    <SpellEffects>
        <!-- The caster must be adjacent to water too. -->
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <bCasterOnly>1</bCasterOnly>
            <bAdjacentToWater>1</bAdjacentToWater>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <bCausesWar>1</bCausesWar>
            <bAdjacentToWater>1</bAdjacentToWater>
            <DamageType>DAMAGE_COLD</DamageType>
            <iDamage>30</iDamage>
            <iDamageLimit>50</iDamageLimit>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_TILE</TargetEntity>
            <bCausesWar>1</bCausesWar>
            <bAdjacentToWater>1</bAdjacentToWater>
            <ConvertImprovementTo>IMPROVEMENT_NONE</ConvertImprovementTo>
            <iSuccessChance>25</iSuccessChance>
        </SpellEffect>
    <SpellEffects>
    <Effect>EFFECT_SPELL1</Effect>
    <Sound>AS3D_SPELL_TSUNAMI</Sound>
    <Button>Art/Interface/Buttons/Spells/Tsunami.dds</Button>
</SpellInfo>

###Damage spell with customized target tiles

#!xml
<SpellInfo>
    <Type>SPELL_PILLAR_OF_FIRE</Type>
    <Description>TXT_KEY_SPELL_PILLAR_OF_FIRE</Description>
    <Civilopedia>TXT_KEY_SPELL_PILLAR_OF_FIRE_PEDIA</Civilopedia>
    <Help>TXT_KEY_SPELL_PILLAR_OF_FIRE_HELP</Help>
    <UnitClassOrPrereqs>
        <UnitClass>UNITCLASS_HIGH_PRIEST_OF_THE_ORDER</UnitClass>
    </UnitClassOrPrereqs>
    <bDisplayWhenDisabled>1</bDisplayWhenDisabled>
    <bHasCasted>1</bHasCasted>
    <TargetType>SPELLTARGET_PYTHON</TargetType>
    <iRange>3</iRange>
    <PyTargetTiles>targetStrongestEnemyStack(pCaster, iRange)</PyTargetTiles>
    <SpellEffects>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <DamageType>DAMAGE_FIRE</DamageType>
            <iDamage>30</iDamage>
            <iDamageLimit>50</iDamageLimit>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_TILE</TargetEntity>
            <ImprovementOrPrereqs>
                <Improvement>IMPROVEMENT_NONE</Improvement>
            </ImprovementOrPrereqs>
            <bFlammableFeature>1</bFlammableFeature>
            <ConvertImprovement>IMPROVEMENT_SMOKE</ConvertImprovement>
            <iSuccessChance>20</iSuccessChance>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
    </SpellEffects>
    <Effect>EFFECT_SPELL1</Effect>
    <Sound>AS3D_SPELL_FIREBALL</Sound>
    <Button>Art/Interface/Buttons/Spells/PillarofFire.dds</Button>
</SpellInfo>

###Improvement creation spell

#!xml
<SpellInfo>
    <Type>SPELL_BLAZE</Type>
    <Description>TXT_KEY_SPELL_BLAZE</Description>
    <Civilopedia>TXT_KEY_SPELL_PLACEHOLDER_PEDIA</Civilopedia>
    <Help>TXT_KEY_SPELL_BLAZE_HELP</Help>
    <PromotionPrereq1>PROMOTION_FIRE1</PromotionPrereq1>
    <PromotionPrereq2>PROMOTION_CHANNELING1</PromotionPrereq2>
    <bDisplayWhenDisabled>1</bDisplayWhenDisabled>
    <bHasCasted>1</bHasCasted>
    <SpellEffects>
        <SpellEffect>
            <TargetEntity>ENTITY_TILE</TargetEntity>
            <FeatureOrPrereqs>
                <Feature>FEATURE_FOREST_ANCIENT</Feature>
                <Feature>FEATURE_JUNGLE</Feature>
                <Feature>FEATURE_FOREST</Feature>
            </FeatureOrPrereqs>
            <ImprovementOrPrereqs>
                <Improvement>IMPROVEMENT_NONE</Improvement>
            </ImprovementOrPrereqs>
            <ConvertImprovementTo>IMPROVEMENT_SMOKE</ConvertImprovementTo>
        </SpellEffect>
    </SpellEffects>
    <Effect>EFFECT_SCORCH</Effect>
    <Sound>AS3D_SPELL_DEFILE</Sound>
    <Button>Art/Interface/Buttons/Spells/Blaze.dds</Button>
</SpellInfo>

###Spell with a complex spell effect order

This spell shows how to abuse spell effect order to allow creating complex chains of events. First it will dispel all dispellable promotions from affected units. After that, it will apply a few dispellable promotions to friendly units. The add promotion effects are coded after the dispel effect in order to prevent having them dispelled. At the end, the spell will kill its caster.

#!xml
<SpellInfo>
    <Type>SPELL_ARCANE_BURST</Type>
    <Description>TXT_KEY_SPELL_ARCANE_BURST</Description>
    <Civilopedia>TXT_KEY_SPELL_ARCANE_BURST_PEDIA</Civilopedia>
    <Help>TXT_KEY_SPELL_ARCANE_BURST_HELP</Help>
    <UnitPrereq>UNIT_ARCANE_SAVANT</UnitPrereq>
    <PromotionAndPrereqs>
        <Promotion>PROMOTION_CHANNELING3</Promotion>
    </PromotionAndPrereqs>
    <bDisplayWhenDisabled>1</bDisplayWhenDisabled>
    <bHasCasted>1</bHasCasted>
    <TargetType>SPELLTARGET_RADIUS</TargetType>
    <iRange>2</iRange>
    <SpellEffects>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <bDispel>1</bDispel>
            <bSetHasCasted>0</bSetHasCasted>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <bImmuneNeutral>1</bImmuneNeutral>
            <bImmuneEnemy>1</bImmuneEnemy>
            <AddPromotion>PROMOTION_SPELLSTAFF</AddPromotion>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <bImmuneNeutral>1</bImmuneNeutral>
            <bImmuneEnemy>1</bImmuneEnemy>
            <AddPromotion>PROMOTION_HASTE</AddPromotion>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <bCasterOnly>1</bCasterOnly>
            <bKill>1</bKill>
        </SpellEffect>
    </SpellEffects>
    <Effect>EFFECT_SPELL2</Effect>
    <Sound>AS3D_SPELL_ESCAPE</Sound>
    <Button>Art/Interface/Buttons/Spells/Escape.dds</Button>
</SpellInfo>

###Spell that requires specific units in the tile

Add to wolf pack is also a complex example of spell effect stacking.

#!xml
<SpellInfo>
    <Type>SPELL_ADD_TO_WOLF_PACK</Type>
    <Description>TXT_KEY_SPELL_ADD_TO_WOLF_PACK</Description>
    <Civilopedia>TXT_KEY_SPELL_PLACEHOLDER_PEDIA</Civilopedia>
    <Help>TXT_KEY_SPELL_ADD_TO_WOLF_PACK_HELP</Help>
    <UnitClassOrPrereqs>
        <UnitClass>UNITCLASS_WOLF</UnitClass>
    </UnitClassOrPrereqs>
    <CivilizationPrereq>CIVILIZATION_DOVIELLO</CivilizationPrereq>
    <bHasCasted>1</bHasCasted>
    <bAbility>1</bAbility>
    <SpellEffects>
        <!-- Enforce that there is a wolf pack without Empower V in the tile. -->
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <UnitOrPrereqs>
                <Unit>UNIT_WOLF_PACK</Unit>
            </UnitOrPrereqs>
            <PromotionAndBlocks>
                <Promotion>PROMOTION_EMPOWER5</Promotion>
            </PromotionAndBlocks>
        </SpellEffect>
        <!-- Increase Empower promotions in the right order. -->
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <UnitOrPrereqs>
                <Unit>UNIT_WOLF_PACK</Unit>
            </UnitOrPrereqs>
            <PromotionOrPrereqs>
                <Promotion>PROMOTION_EMPOWER4</Promotion>
            </PromotionOrPrereqs>
            <PromotionAndBlocks>
                <Promotion>PROMOTION_EMPOWER5</Promotion>
            </PromotionAndBlocks>
            <AddPromotion>PROMOTION_EMPOWER5</AddPromotion>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <UnitOrPrereqs>
                <Unit>UNIT_WOLF_PACK</Unit>
            </UnitOrPrereqs>
            <PromotionOrPrereqs>
                <Promotion>PROMOTION_EMPOWER3</Promotion>
            </PromotionOrPrereqs>
            <PromotionAndBlocks>
                <Promotion>PROMOTION_EMPOWER4</Promotion>
            </PromotionAndBlocks>
            <AddPromotion>PROMOTION_EMPOWER4</AddPromotion>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <UnitOrPrereqs>
                <Unit>UNIT_WOLF_PACK</Unit>
            </UnitOrPrereqs>
            <PromotionOrPrereqs>
                <Promotion>PROMOTION_EMPOWER2</Promotion>
            </PromotionOrPrereqs>
            <PromotionAndBlocks>
                <Promotion>PROMOTION_EMPOWER3</Promotion>
            </PromotionAndBlocks>
            <AddPromotion>PROMOTION_EMPOWER3</AddPromotion>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <UnitOrPrereqs>
                <Unit>UNIT_WOLF_PACK</Unit>
            </UnitOrPrereqs>
            <PromotionOrPrereqs>
                <Promotion>PROMOTION_EMPOWER1</Promotion>
            </PromotionOrPrereqs>
            <PromotionAndBlocks>
                <Promotion>PROMOTION_EMPOWER2</Promotion>
            </PromotionAndBlocks>
            <AddPromotion>PROMOTION_EMPOWER2</AddPromotion>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <UnitOrPrereqs>
                <Unit>UNIT_WOLF_PACK</Unit>
            </UnitOrPrereqs>
            <PromotionAndBlocks>
                <Promotion>PROMOTION_EMPOWER1</Promotion>
            </PromotionAndBlocks>
            <AddPromotion>PROMOTION_EMPOWER1</AddPromotion>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
        <!-- Kill the caster. -->
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <bCasterOnly>1</bCasterOnly>
            <bKill>1</bKill>
        </SpellEffect>
    </SpellEffects>
    <Effect>EFFECT_SPELL1</Effect>
    <Sound>AS3D_SPELL_TRAIN</Sound>
    <bGraphicalOnly>0</bGraphicalOnly>
    <Button>Art/Interface/Buttons/Units/Wolf.dds</Button>
</SpellInfo>

###Terraform spell

This example shows the XML definition of Vitalize with the new system. It demonstrates that spell effect order matters. If spell effects were defined in the opposite order, ice tiles would be converted directly into grass, as the spell would turn them into tundra, then into plains and then into grass.

#!xml
<SpellInfo>
    <Type>SPELL_VITALIZE</Type>
    <Description>TXT_KEY_SPELL_VITALIZE</Description>
    <Civilopedia>TXT_KEY_SPELL_VITALIZE_PEDIA</Civilopedia>
    <Help>TXT_KEY_SPELL_VITALIZE_HELP</Help>
    <PromotionPrereq1>PROMOTION_NATURE3</PromotionPrereq1>
    <bAllowAutomateTerrain>1</bAllowAutomateTerrain>
    <bDisplayWhenDisabled>1</bDisplayWhenDisabled>
    <bHasCasted>1</bHasCasted>
    <SpellEffects>
        <SpellEffect>
            <TargetEntity>ENTITY_TILE</TargetEntity>
            <TerrainOrPrereqs>
                <Terrain>TERRAIN_MARSH</Terrain>
                <Terrain>TERRAIN_PLAINS</Terrain>
            </TerrainOrPrereqs>
            <ConvertTerrain>TERRAIN_GRASS</ConvertTerrain>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_TILE</TargetEntity>
            <TerrainOrPrereqs>
                <Terrain>TERRAIN_DESERT</Terrain>
                <Terrain>TERRAIN_TUNDRA</Terrain>
            </TerrainOrPrereqs>
            <ConvertTerrain>TERRAIN_PLAINS</ConvertTerrain>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_TILE</TargetEntity>
            <FeatureOrPrereqs>
                <Feature>FEATURE_SCRUB</Feature>
            </FeatureOrPrereqs>
            <ConvertFeature>NO_FEATURE</ConvertFeature>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_TILE</TargetEntity>
            <TerrainOrPrereqs>
                <Terrain>TERRAIN_ICE</Terrain>
                <Terrain>TERRAIN_TUNDRA</Terrain>
            </TerrainOrPrereqs>
            <ConvertTerrain>TERRAIN_TUNDRA</ConvertTerrain>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
    </SpellEffects>
    <Effect>EFFECT_VITALIZE</Effect>
    <Sound>AS3D_SPELL_SANCTIFY</Sound>
    <Button>Art/Interface/Buttons/Spells/Vitalize.dds</Button>
</SpellInfo>

###Fertility

Fertility is a Rise from Erebus spell which is also present in ExtraModMod. It allows to transform food resources of the same "group" into another resource of the same group. Only the transformation between resources of the "cereal" group is shown in this example.

This example is provided to show that a complex terraformation spell could be added and the AI would be able to know how to use it automatically without having to introduce any hardcoded terraform AI code. The AI would only need to check tiles in its empire that fulfill the requirement (tiles with bonuses), check how they would be affected by the spell and check if the change benefits it (yield changes, getting different resources, being able to trade resources that other players do not have and so on).

#!xml
<SpellInfo>
    <Type>SPELL_FERTILITY</Type>
    <Description>TXT_KEY_SPELL_FERTILITY</Description>
    <Civilopedia>TXT_KEY_SPELL_PLACEHOLDER_PEDIA</Civilopedia>
    <Help>TXT_KEY_SPELL_FERTILITY_HELP</Help>
    <PromotionPrereq1>PROMOTION_CREATION2</PromotionPrereq1>
    <bAllowAutomateTerrain>1</bAllowAutomateTerrain>
    <bDisplayWhenDisabled>1</bDisplayWhenDisabled>
    <bHasCasted>1</bHasCasted>
    <SpellEffects>
        <SpellEffect>
            <TargetEntity>ENTITY_TILE</TargetEntity>
            <BonusOrPrereqs>
                <Bonus>BONUS_CORN</Bonus>
            </BonusOrPrereqs>
            <ConvertBonus>BONUS_WHEAT</ConvertBonus>
            <bFinal>1</bFinal>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_TILE</TargetEntity>
            <BonusOrPrereqs>
                <Bonus>BONUS_RICE</Bonus>
            </BonusOrPrereqs>
            <ConvertBonus>BONUS_CORN</ConvertBonus>
            <bFinal>1</bFinal>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_TILE</TargetEntity>
            <BonusOrPrereqs>
                <Bonus>BONUS_WHEAT</Bonus>
            </BonusOrPrereqs>
            <ConvertBonus>BONUS_RICE</ConvertBonus>
            <bFinal>1</bFinal>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
    </SpellEffects>
    <Effect>EFFECT_SPELL1</Effect>
    <Sound>AS2D_POSITIVE_DINK</Sound>
    <Button>Art/Interface/Buttons/Spells/fertility.dds</Button>
</SpellInfo>

###Unsupported spell

Escape is provided as an example of an spell effect which is not possible with this proposal and would still require to use python. It shows the ExtraModMod version of the effect of this spell (allows to escape cages along with the usual teleport effect). Even in this case, it is possible to use the new spell effect system to separate the effects of the spell and give them separate prerrequisites, which would allow to avoid the costly python check for the cage in most cases.

#!xml
<SpellInfo>
    <Type>SPELL_ESCAPE</Type>
    <Description>TXT_KEY_SPELL_ESCAPE</Description>
    <Civilopedia>TXT_KEY_SPELL_ESCAPE_PEDIA</Civilopedia>
    <Help>TXT_KEY_SPELL_ESCAPE_HELP</Help>
    <UnitPrereq>UNIT_CHANTER</UnitPrereq>
    <bDisplayWhenDisabled>1</bDisplayWhenDisabled>
    <bHasCasted>1</bHasCasted>
    <PyRequirement>reqEscape(pCaster)</PyRequirement>
    <SpellEffects>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <ImprovementOrPrereqs>
                <Improvement>IMPROVEMENT_CAGE</Improvement>
            </ImprovementOrPrereqs>
            <RemovePromotion>PROMOTION_HELD</RemovePromotion>
            <PyUnitEffect>tryRemoveCage(pCaster, pUnit)</PyUnitEffect>
            <bNotRequired>1</bNotRequired>
        </SpellEffect>
        <SpellEffect>
            <TargetEntity>ENTITY_UNIT</TargetEntity>
            <bCasterOnly>1</bCasterOnly>
            <PyUnitEffect>spellTeleport(pCaster, pUnit, 'Capital')</PyUnitEffect>
        </SpellEffect>
    </SpellEffects>
    <Effect>EFFECT_SPELL2</Effect>
    <Sound>AS3D_SPELL_ESCAPE</Sound>
    <Button>Art/Interface/Buttons/Spells/Escape.dds</Button>
</SpellInfo>

##DLL changes

For now, this section is just a stub.

###CvConditionArray

The goal of the CvConditionArray class is to store prerrequisites related to an specific entity category in a way which is both fast to check and not memory intensive. Each entity of the category is referred by its entity ID, which is an integer (FeatureTypes, PromotionTypes and so on). CvConditionArray is intended to be used with entity categories with a lot of different types, but for which other entities only "have" a small number of them. For example, there are a lot of promotions in the game, but units usually have only a few of them.

When creating a new CvConditionArray, it will require a sizeLimit value, which should be initialized depending on the number of elements of the category of the CvConditionArray (which can be obtained by calls to the corresponding method of CvGlobals such as GC.getNumPromotionInfos() or GC.getNumBuildingInfos()).

Besides being the size limit of the CvConditionArray, sizeLimit also limits the entities accepted by the array. Trying to add or check values over sizeLimit - 1 will throw an FAssert.

For each entityID, CvConditionArray may contain either true or false. True means that the entity is part of the prerequisites, and that it MUST be present. False means that the entity is part of the prerequisites, and it MUST NOT be present. If the entity is not present in the CvConditionArray, then it is not part of the prerequisites.

Internally, CvConditionArray is implemented as a sparse array of bools. The data is stored into a std::map<int, bool>. The internal map of an empty CvConditionArray, or one for which all entities are UNDEFINED, contains no elements. Entities that are part of the prerequisites and must be present are stored as an [entityID, true] pair, while entities that must not be present are stored as an [entityID, false] pair.

It is not possible to check specific values of the CvConditionArray (that would be slow, and it is not the intended usage of this class). CvConditionArray can check itself against any boolean array which has a size equal to its sizeLimit. These arrays are intended to be the pseudomap that indicate if a certain entity "has" those entities or not. An example would be the boolean array in which CvUnit stores the promotion that a certain unit has. It will return true

###Efficiency

In Big O Notation, the efficiency of a given method is represented as a function of the size of the input data as the variable. For example, a for that loops through all game entities would have O(N) efficiency, while a double for that checks each promotion against all existing promotions would have a O(N^2) efficiency. A certain O can roughly be interpreted as the number of iterations that the code will do depending on the size of the data.

I will call N the total number of entities in the game of a certain type (what is called sizeLimit in the definition of CvConditionArray). n will be the number of entities that are actually part of the prerequisite.

With the current implementation of spell prerequisites (PromotionPrereq1, PromotionPrereq2 and so on) in order to check if a caster meets a certain group of prerequisites we require a computation time of in the worst case (the worst case being that all the prerequisites are true and therefore we cannot bail out early answering false) of n, or O(n) (as for each promotion we just get the right position of the promotion pseudomap in CvUnit to see if the unit has it or not).

If instead of CvConditionArray we were using an array of integers to store prerequisites (-1 for "must not be present", 0 for "not part of the prerequisite" and 1 for "must be present"), and we checked each value of the array against each value of the promotion pseudomap in CvUnit we would get an efficiency in the worst case of O(N^2), which in short could be considered horrible.

With the suggested comparison method of checking a CvConditionArray, we iterate through it directly and check the values against the pseudomap so the efficiency would be O(n) in the worst case like in the current implementation, although with a time for each iteration which would be bigger than the original.

Since the underlying data structure is a map, checking if a certain value is present in a CvConditionArray and getting its value has a complexity of O(log(N)) for each check in the worst case (which would be having all game entities as prerequisites), so we would have an efficiency of O(N * log(N)) for checking all entities against it. This is why checking values directly should be disabled unless there is a valid case for using them.

###Memory usage

The basic memory usage of a std:map is: (sizeof(A) + sizeof(B) + ELEMENT_OVERHEAD) * n + CONTAINER_OVERHEAD (as seen in http://stackoverflow.com/questions/720507/how-can-i-estimate-memory-usage-of-stlmap). As long as n is a lot smaller than N, using a CvConditionArray for entities which have a lot of types should improve memory usage. It is probably not worth using it for entities with small quantities such as UnitClasses.

Updated