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

Guide to event modding in BtS

Discussion in 'Civ4 - Modding Tutorials & Reference' started by Solver, Jul 9, 2007.

  1. Solver

    Solver Civ4/5 beta tester

    Joined:
    Mar 22, 2002
    Messages:
    1,260
    Location:
    Latvia, Riga
    Following is my guide to modding events in Beyond the Sword. The guide is part of the Modders Guide to Beyond the Sword. Any updates will be posted to this thread first, and it can be used for feedback and questions related to this guide and event modding. The guide follows...


    -------------------------------

    Intro

    Random events are one of the new features in Civ4: Beyond the Sword. The game ships with over 150 events, which vary from happiness and diplomatic bonuses to free promotions and technological breakthroughs. The event system provides a great foundation for modders to build open, as all events can be modded and new ones can be added.

    You can create a wide array of possible events through XML modding only, but events can also be enhanced by Python for more versatility. The last section of this guide has some examples of events that can be modded in. The events described therein, along with some others, can be found in an event mod created by me. Think of the mod as an addendum to this guide, if you will. It can be found here.

    The basics

    It’s important to understand the two basic building blocks of the event system – triggers and events. “Triggers” are a collection of parameters indicating conditions when they can activate. So, triggers check whether you have the required technologies, population, etc. If the trigger’s conditions are met, it has a chance of activating. When a trigger is activated, it gives the player choice between one or more “events” (well, when there is only one event, there’s no choice). Events themselves are what provides the specific results that then affect the player.

    Players may be inclined to think of the event system as consisting of “events”and “choices” – that’s not really accurate. Every choice is a separate event, and a trigger is what gives those choices.

    As most other things in Civ4, events are primarily handled in the SDK. Events are described by the CvEventInfo class, which mimics the XML description of events. Below is the description of event tags in the XML file. If you can read C++, you may want to take a look at the definition of CvEventInfo. The correspondence between member functions of that class and the XML values should be fairly obvious.

    Event triggers are kept in Civ4EventTriggerInfos.xml and that file, along with triggers in general, will be described after the event description explanations.


    Civ4EventInfos.xml


    Assets\XML\Events\Civ4EventInfos.xml is the main file describing all the in-game events. This section will describe the tags in that file, explaining their meaning.

    Type- this is the event type identifier. Takes the form of EVENT_NAME

    Description- identifier of the text string which contains the event description. Typically takes the form of TXT_KEY_EVENT_NAME, and description text strings can be found in Assets\XML\Text\Civ4GameText_Events_BTS.xml

    LocalInfoText- identifier of the text string, if any, to be shown to the affected player.

    WorldNewsTexts- specifies text, if any, to be shown to every player in the world (who has met the affected civilization) when the event occurs.

    OtherPlayerPopup- specifies text, if any, to show in a popup to the other player affected. In BtS, is used for the Partisans event.

    QuestFailText- specifies text, if any, to be displayed if the quest has been failed. Obviously, used for quests.

    bQuest- can be either 0 or 1. If set to 1, then the event is actually a quest.

    bGlobal- can be either 0 or 1. If set to 1, then the event will apply to all players. That is used for events that complete a quest in order to reset it for every player.

    bTeam- can be either 0 or 1. If set to 1, then it will apply to all players on the same team. It’s not used in any of the events shipping with BtS.

    bPickCity- can be either 0 or 1. If set to 1, then the event affects a city of the player for whom the event was triggered.

    bPickOtherPlayerCity- can be either 0 or 1. If set to 1, the event affects a foreign city. For example, you can be given the choice of spending some espionage points to worsen the effects this event deals to the foreign city. Used in conjunction with <bPickCity>, creates an event that affects the player&#8217;s city and a foreign city. Famine is one example of such an event in BtS.

    bDeclareWar- can be either 0 or 1. If set to 1, will cause the affected player and the other player to go to war.

    iGold- is a number. Specifies the base amount of gold that the affected player gains. If set to a negative number, the player will lose the gold. Can also optionally affect the other player &#8211; see below.

    bGoldToPlayer- can be either 0 or 1. Works in conjunction with <iGold>. If set to 1, then the gold value of <iGold> will be given to the other player. Since it requires another player, it&#8217;s only useful for events that involve another player.

    iRandomGold- is a number. Specifies the cap for an extra random amount of gold that is added to iGold to produce the total amount of gold.

    iCulture- is a number. Specifies the amount of culture to be added to a city &#8211; only useful for events targeting a city.

    iEspionagePoints- is a number. Specifies the amount of espionage points that the affected player will gain towards the other player.

    bGoldenAge- can be either 0 or 1. If set to 1, triggers a golden age for the affected player.

    iFreeUnitSupport- is a number. Specifies how many extra free units the player is allowed to support.

    iInflationMod- is a number. Specifies by how much the affected player&#8217;s inflation modifier is changed.

    iSpaceProductionMod- is a number. Specifies by how much the player&#8217;s spaceship part production modifier is changed. A value of 10, for example, would speed SS production up by 10%.

    Tech- is a technology identifier. NONE for no technology. If set, explicitly specifies a technology towards which you can gain beakers.

    TechFlavors- if not empty, contains one or more flavor types. For events that result in progress towards a technology, flavors affect which technology will be picked. For example, FLAVOR_RELIGION with a value of 1 will make you more likely to have progress towards a religious technology.

    iTechPercent- is a number. Specifies by how many percent closer the player will become to the technology. The technology can be set explicitly (see <Tech>) or chosen randomly. Negative numbers can be used to set research back.

    iTechCostPercent- is a number. Specifies how many percent of the technology&#8217;s beaker cost to convert to a gold value.

    iTechMinTurnsLeft- is a number. Specifies that the event may only affect techs for which the player needs at least this many turns to research. For example, setting to 2 for an event that advances research progress would mean that progress won&#8217;t be advanced for a tech that you&#8217;re about to complete in 2 turns.

    PrereqTech- is a technology identifier. NONE for no technology. If set, then the technology is required for this event. Used to create event choices that the player can not pick unless he has the tech.

    UnitClass- is a unit class identifier. NONE for no unit class. Specifies a unit class for receiving free units &#8211; see below.

    iNumFreeUnits- is a number. Specifies how many free units of the class given in <UnitClass> the player should receive. For example, with <UnitClass> set to UNITCLASS_CROSSBOWMAN and <iNumFreeUnits> to 2, the player would get 2 free Crossbowmen, or 2 free Chu-Ko-Nu if Chinese.

    bDisbandUnit- can be either 0 or 1. If set to 1, disbands (kills) the affected unit.

    iUnitExperience- is a number. Specifies the amount of free XP received by the affected unit.

    iUnitImmobileTurns- is a number. Specifies the number of turns that the affected unit will remain immobile. Immobile units can still defend.

    UnitPromotion- is a promotion identifier. For events that affect a specific unit (as determined by the trigger), can be used to apply a promotion to that specific unit. It&#8217;s not used in BtS, however &#8211; instead, Python callbacks are used (example: the Champion event).

    UnitName- is a text string identifier. If specified, will set the affected unit&#8217;s name to the specified string.

    UnitCombatPromotions- if non-empty, contains free promotions that all units of a selected combat type receive. Format to fill is as follows:
    Code:
    <UnitCombatPromotions> 
       <UnitCombatPromotion> 
          <UnitCombat>UNITCOMBAT_ARCHER</UnitCombat> 
          <UnitPromotion>PROMOTION_COMBAT1</UnitPromotion> 
    </UnitCombatPromotion> 
    </UnitCombatPromotions> 
    
    The above would give all archery units a free Combat I promotion.

    UnitClassPromotions- similar to above. If non-empty, contains free promotions for all units of a selected unit class to receive. Format as follows:
    Code:
    <UnitClassPromotions> 
                <UnitClassPromotion> 
                   <UnitClass>UNITCLASS_AXEMAN</UnitClass> 
                   <UnitPromotion>PROMOTION_SHOCK</UnitPromotion> 
                </UnitClassPromotion> 
    </UnitClassPromotions> 
    
    That would give all Axeman-class units Shock.

    BuildingClass- is a building class identifier. NONE for no building. Explicitly gives a building class to be affected by the event. Used with <iBuildingChange>, see below.

    iBuildingChange- is a number. Specifies how many buildings of the class specified in <BuildingClass> to add. Since a city can only contain 1 of a building, the only useful values are 1 to add a building and -1 to remove it. So, if <BuildingClass> is a BUILDINGCLASS_FORGE and <iBuildingChange> is -1, the event would remove the affected city&#8217;s Forge.

    BuildingExtraYields- if non-empty, contains information that modifies yield given by a building. For example, a building can be made to produce extra food or hammers. Format to fill as follows:
    Code:
    <BuildingExtraYields> 
                <BuildingExtraYield> 
                   <BuildingClass>BUILDINGCLASS_HARBOR</BuildingClass> 
                   <YieldType>YIELD_COMMERCE</YieldType> 
                   <iExtraYield>2</iExtraYield> 
                </BuildingExtraYield> 
    </BuildingExtraYields> 
    
    The above would give your Harbours 2 extra commerce. If <bPickCity> is 0, the change will apply to all buildings of the class in your civ. If <bPickCity> is 1, only the building in the affected city will be modified.

    BuildingExtraCommerces- similar to above, if non-empty, contains information tat modifies commerce outputs given by a building. Note that this is not for pure commerce (to change that, use BuildingExtraYields and YIELD_COMMERCE as per the above example), but rather for commerce types &#8211; research, culture, gold or espionage. Format to fill as follows:
    Code:
    <BuildingExtraCommerces> 
                <BuildingExtraCommerce> 
                   <BuildingClass>BUILDINGCLASS_MARKET</BuildingClass> 
                   <CommerceType>COMMERCE_GOLD</CommerceType> 
                   <iExtraCommerce>1</iExtraCommerce> 
          </BuildingExtraCommerce> 
    </BuildingExtraCommerces> 
    
    The above would add 1 gold to the output of your Markets. Just as with the <BuildingExtraYields>, if <bPickCity> is 0, the change will apply to all buildings of the class in your civ. If <bPickCity> is 1, only the building in the affected city will be modified.

    BuildingExtraHappies- another similar tag, if non-empty, assigns extra happiness to buildings. Format as follows:
    Code:
    <BuildingExtraHappies> 
                <BuildingExtraHappy> 
                   <BuildingClass>BUILDINGCLASS_THEATRE</BuildingClass> 
                   <iHappy>1</iHappy> 
                </BuildingExtraHappy> 
    </BuildingExtraHappies> 
    
    That&#8217;s to add 1 happiness to a Theatre. Once again, if <bPickCity> is 0, the change will apply to all buildings of the class in your civ. If <bPickCity> is 1, only the building in the affected city will be modified.

    BuildingExtraHealths- works just like <BuildingExtraHappies> above, but adds extra health.
    Code:
    <BuildingExtraHealths> 
                <BuildingExtraHealth> 
                   <BuildingClass>BUILDINGCLASS_HOSPITAL</BuildingClass> 
                   <iHealth>1</iHealth> 
                </BuildingExtraHealth> 
    </BuildingExtraHealths> 
    
    That would give +1 health to your Hospitals. If <bPickCity> is 0, the change will apply to all buildings of the class in your civ. If <bPickCity> is 1, only the building in the affected city will be modified.

    iHappy- is a number. Specifies extra happiness to give. If <bPickCity> is 0, the change will apply to the entire civ. If bPickCity> is 1, only the affected city&#8217;s happiness will be modified.

    iHealth- is a number. Specifies extra health to give. If <bPickCity> is 0, the change will apply to the entire civ. If <bPickCity> is 1, only the affected city&#8217;s health will be modified.

    iHurryAnger- is a number. Specifies extra whipping unhappiness to give. Unhappy citizens created by events using this variable will have the &#8220;We can not forget your cruel oppression!&#8221; unhappiness. You can be extra evil by setting <bPickCity> to 0, which would give the penalty in all cities. Ack!

    iHappyTurns- is a number. Specifies the amount of turns to give temporary happiness. The amount of happy faces given is specified in Assets\XML\GlobalAssets.xml as the TEMP_HAPPY variable. It defaults to 1 in BtS. Therefore, setting <iHappyTurns> to 10 would ensure +1 happiness for 10 turns. Once again, if <bPickCity> is 0, the change will apply to the entire civ. If <bPickCity> is 1, only the affected city&#8217;s happiness will be modified.

    iRevoltTurns- is a number. Specifies the amount of turns that the city will spend in revolt (no production, etc., like newly captured cities). Must be city-targeted, that is, used in conjunction with <bPickCity> or <bPickOtherPlayerCity> set to 1.

    iMinPillage- is a number. Specifies the minimum amount of terrain improvements that will be pillaged (removed). If used with a city-targeting event, then the pillaged improvement will be in the city&#8217;s radius. If the event doesn&#8217;t target a city, the improvement may be anywhere.

    iMaxPillage- is a number. Specifies the maximum amount of terrain improvement that will be pillaged(removed). Works in conjunction with <iMinPillage> and must be equal or greater than <iMinPillage>, see above. Setting both variables to an equal value will result in exactly that many improvements getting pillaged.

    iFood- is a number. Specifies the amount of stored food to be added in a city. If <bPickCity> is 0, the change will apply to the entire civ. If <bPickCity> is 1, only the affected city&#8217;s food stores will be modified.

    iFoodPercent- is a number. Specifies by how many percent to modify the stored food in a city. You can remove all the stored food by setting this value to -100. If <bPickCity> is 0, the change will apply to the entire civ. If <bPickCity> is 1, only the affected city&#8217;s food stores will be modified.

    FreeSpecialistCounts- if non-empty, describes the free specialists added to a city. Can only be used with events that target a city. Format is as follows:
    Code:
    <FreeSpecialistCounts> 
                <FreeSpecialistCount> 
                   <SpecialistType>SPECIALIST_SCIENTIST</SpecialistType> 
                   <iFreeSpecialistCount>1</iFreeSpecialistCount> 
                </FreeSpecialistCount> 
    </FreeSpecialistCounts> 
    
    That would add a free Scientist to the city.

    FeatureType- is a feature type identifier, NONE for no feature types. Sets the feature type that the event will add or remove in a plot, used in conjunction with <iFeatureChange>, see below.

    iFeatureChange- is a number. If used, should only take the values of 1 or -1. Setting to 1 would add the feature described in <FeatureType> to the affected plot, setting to -1 would remove the feature.

    ImprovementType- is an improvement type identifier, NONE for no improvement. Sets the improvement type that the event will add or remove in a plot, used in conjunction with <iImprovementChange>, see below.

    iImprovementChange- is a number. If used, should only take the values of 1 or -1. Setting to 1 would add the improvement described in <ImprovementType> to the affected plot, setting to -1 would remove the improvement.

    BonusType- is a bonus (resource) type identifier, NONE for no bonus. Sets the bonus type that the event will add or remove in a plot, used in conjunction with <iBonusChange>, see below.

    iBonusChange- is a number. If used, should only take the values of 1 or -1. Setting to 1 would add the bonus described in <BonusType> to the affected plot, setting to -1 would remove the bonus.

    RouteType- is a route type identifier, NONE for no route. Sets the route type that the event will add or remove in a plot, used in conjunction with <iRouteChange>, see below.

    iRouteChange- is a number. If used, should only take the values of 1 or -1. Setting to 1 would add the route described in <RouteType> to the affected plot, setting to -1 would remove the route.

    BonusRevealed- is a bonus type identifier, NONE for no bonus. If used, will reveal the specified bonus to the affected player, even if he has no technology to normally see the bonus.

    BonusGift- is a bonus type identifier, NONE for no bonus. If used, will make the affected player gift the specified bonus to the other player. As such, it can only be used with events that affect two players.

    PlotExtraYields- if non-empty, contains information that modifies the yields of the affected plot. Format is as follows:
    Code:
    <PlotExtraYields> 
                <PlotExtraYield> 
                   <YieldType>YIELD_COMMERCE</YieldType> 
                   <iExtraYield>1</iExtraYield> 
                </PlotExtraYield> 
    </PlotExtraYields> 
    
    The above would add 1 commerce to the affected plot.

    iConvertOwnCities- is a number. Specifies the number of the player&#8217;s cities that will be converted to the trigger religion, the religion being added to the city if not already present. The religion is determined in event triggers.

    iConvertOtherCities- is a number. Specifies the number of foreign cities that will be converted to the trigger religion. The religion is determined in event triggers.

    iMaxNumReligions- is a number. Specifies the maximum amount of religions that may be present in a city in order for it to be converted by either <iConvertOwnCities> or <iCnvertOtherCities>. If you wish to ignore the amount of religions already in the city, set this variable to -1.

    iOurAttitudeModifier- is a number. Specifies the attitude modifier of the affected player towards the other player.

    iAttitudeModifier- is a number. Specifies the attitude modifier of the other player towards the affected player.

    iTheirEnemyAttitudeModifier- is a number. Specifies the attitude modifier of the affected player towards the other player&#8217;s worst enemy and vice versa. For example, if set to -1, Civ A is the affected player and Civ B is the other player, then Civ A will have -1 towards Civ B&#8217;s worst enemy and Civ B&#8217;s worst enemy will also have a -1 modifier towards Civ A.

    iPopulationChange- is a number. Specifies the population change in the city &#8211; negative numbers indicate population loss. If <bPickCity> is 0, the change will apply to the entire civ. If <bPickCity> is 1, only the affected city&#8217;s population will be modified.

    AdditionalEvents- if non-empty, specifies extra events that will have a chance of occuring if that event happens. For example,
    Code:
    <AdditionalEvents> 
                <EventChance> 
                   <Event>EVENT_AIRLINER_CRASH_4</Event> 
                   <iEventChance>50</iEventChance> 
                </EventChance> 
    </AdditionalEvents> 
    
    means that EVENT_AIRLINER_CRASH_4 will be triggered with a likelihood of 50%.

    EventTimes- if non-empty, it can be used to set up delayed events. Has to be used along with <AdditionalEvents> above. Fill as follows:
    Code:
    <EventTimes> 
       <EventTime> 
          <Event>EVENT_MOTOR_OIL_1</Event> 
          <iEventTime>5</iEventTime> 
       </EventTime> 
    </EventTimes> 
    
    In case the event filled in <AdditionalEvents> does not trigger due to the random roll, the event timer will kick in, making it occur later.

    ClearEvents- if non-empty, specifies the events which get reset and the probability of that happening. For example:
    Code:
    <ClearEvents> 
                <EventChance> 
                   <Event>EVENT_DUSTBOWL_2</Event> 
                   <iEventChance>100</iEventChance> 
                </EventChance> 
    </ClearEvents> 
    
    This resets the EVENT_DUSTBOWL_2 unconditionally, which means that the event is considered to not have occurred.

    PythonCallback- If non-empty, specifies the Python function that is called when the event occurs. See the section on Python for more information.

    PythonExpireCheck- if non-empty, specifies the Python function that is called to check whether the event expires. See the section on Python for more information.

    PythonCanDo- If non-empty, specifies the Python function that is called to verify whether the event can be triggered. See the section on Python for more information.

    PythonHelp- If non-empty, specifies the Python function that generates the help text for this event. See the section on Python for more information.

    Button- Specifies the art file which is displayed as the event&#8217;s button.

    iAIValue- is a number. Reflects the AI trigger value for this event.
     
  2. Solver

    Solver Civ4/5 beta tester

    Joined:
    Mar 22, 2002
    Messages:
    1,260
    Location:
    Latvia, Riga
    CIV4EventTriggers.xml

    So that&#8217;s it as far as event descriptions go in the XML. However, that is only one side of the event description. The other side are triggers. Triggers are described in Assets\XML\Civ4EventTriggerInfos.xml. In essence, every trigger is a set of conditions. If these conditions are true, then events tied to that particular trigger can occur (some have extra Python checks, that&#8217;s in the Python section). Events that are tied to a trigger are also described in this file. Predictably enough, the SDK class for event triggers is CvEventTriggerInfo.
    So, here are the tags describing triggers.

    Type- this is the trigger type identifier. Takes the form of EVENTTRIGGER_NAME

    WorldNewsTexts- is a text string identifier, which spiecifies the text to be displayed to all players when the event occurs.

    TriggerTexts- text string identifiers can be specified for additional trigger messages, in particular, texts tied to an era. For instance, the Ruins effect:
    Code:
    <TriggerTexts> 
       <TriggerText> 
          <Text>TXT_KEY_EVENT_TRIGGER_CITY_RUINS_ANCIENT_1</Text> 
          <Era>ERA_ANCIENT</Era> 
       </TriggerText> 
       <TriggerText> 
          <Text>TXT_KEY_EVENT_TRIGGER_CITY_RUINS_ANCIENT_1</Text> 
          <Era>ERA_CLASSICAL</Era> 
       </TriggerText> 
       <TriggerText>                  <Text>TXT_KEY_EVENT_TRIGGER_CITY_RUINS_ANCIENT_1</Text> 
                   <Era>ERA_MEDIEVAL</Era> 
                </TriggerText> 
                <TriggerText> 
                   <Text>TXT_KEY_EVENT_TRIGGER_CITY_RUINS_ANCIENT_1</Text> 
                   <Era>ERA_RENAISSANCE</Era> 
                </TriggerText> 
                <TriggerText> 
                   <Text>TXT_KEY_EVENT_TRIGGER_CITY_RUINS_INDUSTRIAL_1</Text> 
                   <Era>ERA_INDUSTRIAL</Era> 
                </TriggerText> 
                <TriggerText> 
                   <Text>TXT_KEY_EVENT_TRIGGER_CITY_RUINS_INDUSTRIAL_1</Text> 
                   <Era>ERA_MODERN</Era> 
                </TriggerText> 
                <TriggerText> 
                   <Text>TXT_KEY_EVENT_TRIGGER_CITY_RUINS_INDUSTRIAL_1</Text> 
                   <Era>ERA_FUTURE</Era> 
                </TriggerText> 
    </TriggerTexts> 
    Here, separate text is being given for every era, so that the correct text would appear at the appropriate time.

    bSinglePlayer- can be either 0 or 1. If set to 1, the event can only trigger in singleplayer games.

    iPercentGamesActive- is a number from 0 to 100. When you start a new game, not all events will be in it. This number determines the probability of this event trigger being included in a new game. Thus you can create events that will be rarely seen &#8211; or, on the contrary, be eligible to occur in every game.

    iWeight- is a number that affects the probability of this trigger triggering. Applies to events that are active in this game only (as does everything else). Set to -1 if the trigger needs to always occur if the other conditions are met (for example, triggers that determine quest completion should be with -1).

    bProbabilityUnitMultiply- can be either 0 or 1. If set to 1, the trigger probability increases as the player builds more units.

    bProbabilityBuildingMultiply- can be either 0 or 1. If set to 1, the trigger probability increases as the player builds more buildings.

    Civic- is a civic identifier, NONE for no civic. If used, specifies the civic required for the trigger to activate.

    iMinTreasury - is a number. Specifies the minimum amount of gold in the player&#8217;s treasury for the trigger to activate.

    iMinPopulation- is a number. Specifies the minimum total population that a player must have for the trigger to activate.

    iMaxPopulation- is a number. Specifies the maximum total population that a player is allowed to have for the trigger to activate.

    iMinMapLandmass- is a number. Specifies the minimum amount of land areas that must be on the map for the trigger to activate. Can be used, essentially, to specify the minimum amount of continents required.

    iMinOurLandmass- is a number. Specifies the minimum amount of land areas that must be under player&#8217;s control (specifically, have cities on them) for the trigger to activate.

    iMaxOurLandmass- is a number. Specifies the maximum amount of land areas that the player is allowed to control for the trigger to activate. Use -1 to have no restrictions.

    MinDifficulty- is a difficulty level specifier, NONE for no difficulty. If used, specifies the minimum difficulty level required for the event trigger to activate,

    iAngry- is a number. Specifies the minimum anger level (unhappiness &#8211; happiness) in a city for the trigger to activate. Applies to triggers that target a city.

    iUnhealthy- is a number. Specifies the minimum unhealthiness level (bad health &#8211; good health) in a city for the trigger to activate. Applies to triggers that target a city.

    UnitsRequired- if non-empty, contains information about required units to trigger. So, if specified, events can only be triggered for the listed unit classes. Example:
    Code:
    <UnitsRequired> 
                <UnitClass>UNITCLASS_GALLEY</UnitClass> 
                <UnitClass>UNITCLASS_TRIREME</UnitClass> 
                <UnitClass>UNITCLASS_CARAVEL</UnitClass> 
                <UnitClass>UNITCLASS_GALLEON</UnitClass> 
                <UnitClass>UNITCLASS_PRIVATEER</UnitClass> 
                <UnitClass>UNITCLASS_FRIGATE</UnitClass> 
                <UnitClass>UNITCLASS_SHIP_OF_THE_LINE</UnitClass> 
                <UnitClass>UNITCLASS_IRONCLAD</UnitClass> 
                <UnitClass>UNITCLASS_TRANSPORT</UnitClass> 
                <UnitClass>UNITCLASS_DESTROYER</UnitClass> 
                <UnitClass>UNITCLASS_STEALTH_DESTROYER</UnitClass> 
                <UnitClass>UNITCLASS_BATTLESHIP</UnitClass> 
                <UnitClass>UNITCLASS_MISSILE_CRUISER</UnitClass> 
                <UnitClass>UNITCLASS_SUBMARINE</UnitClass> 
                <UnitClass>UNITCLASS_ATTACK_SUBMARINE</UnitClass> 
                <UnitClass>UNITCLASS_CARRIER</UnitClass> 
    </UnitsRequired> 
    Such a trigger would make the event only affect the listed naval units.

    iNumUnits- is a number. Specifies the amount of unts that must be on a plot for the trigger to activate &#8211; used for events which pick a plot. If used in conjunction with <UnitsRequired>, only units of the listed classes will be counted.

    iNumUnitsGlobal- is a number. Specifies the amount of units that the player must have for the trigger to activate. If used in conjunction with <UnitsRequired>, only units of the listed classes will be counted.

    iUnitDamagedWeight- is a number. Denotes the probability of the trigger activating for wounded units. Set to 1 for event triggers that you wish to deal with damaged units.

    iUnitDistanceWeight- is a number. For events that affect a plot, increases the probability (if set to 1) of the trigger activating if a unit is far away from the plot.

    iUnitExperienceWeight- is a number. When set to 1, increases the probability of triggering for a highly-experienced unit.

    bUnitsOnPlot- can be either 0 or 1. When set to 1, means that the trigger checks for the presence of units (listed in <UnitsRequired>) on the affected plot.

    BuildingsRequired- if non-empty, contains informations about the buildings required to trigger. Example:
    Code:
    <BuildingsRequired> 
                <BuildingClass>BUILDINGCLASS_FORGE</BuildingClass> 
    </BuildingsRequired> 
    That would denote that a Forge is required for the trigger.

    iNumBuildings- is a number used in conjunction with <BuildingsRequired>. Set to 1 to indicate that 1 building of that type is required in a city, for city-picking events.

    iNumBuildingsGlobal- is a number. Specifies the minimum amount of buildings listed in <BuildingsRequired> that the player must have in total for the trigger to activate.

    iNumPlotsRequired- is a number. Should be set to 1 for triggers that require a plot that meets specific conditions. See below for tags that specify these conditions.

    bOwnPlot- can be either 0 or 1. If set to 1, specifies that the plot must be owned by the affected player to meet conditions.

    iPlotType- is a number. Specifies the plot type that the plot must have to meet the condition. Set to -1 to ignore plot type. Unless otherwise modded, 0 stands for peaks (PLOT_PEAK), 1 for hills (PLOT_HILLS), 2 for land plots (PLOT_LAND) and 3 for ocean plots (PLOT_OCEAN). The enumeration listing plot types is PlotTypes in the SDK and Python.

    FeaturesRequired- if non-empty, specifies the features that the plot must have to meet the condition. For example:
    Code:
    <FeaturesRequired> 
                <FeatureType>FEATURE_FOREST</FeatureType> 
    </FeaturesRequired> 
    That is to require a plot to have Forest. Note that you can also explicitly require a plot not to have any features as follows:
    Code:
    <FeaturesRequired> 
                <FeatureType>NONE</FeatureType> 
    </FeaturesRequired> 
    Please note that flood plains also count as a feature and the above example would exclude any floodplain plots.

    TerrainsRequired- if non-empty, specifies the terrain type that the plot must have to meet the condition. For example:
    Code:
    <TerrainsRequired> 
    <TerrainType>TERRAIN_GRASS</TerrainType> 
          <TerrainType>TERRAIN_PLAINS</TerrainType> 
    </TerrainsRequired> 
    to indicate that Grasslands or Plains plots are eligible for the trigger.

    ImprovementsRequired- if non-empty, specifies the improvements that the plot must have to meet the condition. As an example:
    Code:
    <ImprovementsRequired> 
          <ImprovementType>IMPROVEMENT_PLANTATION</ImprovementType> 
    </ImprovementsRequired> 
    That is to require a Plantation. Note that you can, just like with Features, set the variable to NONE to explicitly require no improvement to be present.

    BonusesRequired- if non-empty, specifies the bonus (resource) types that the plot must have to meet the condition. So,
    Code:
    <BonusesRequired> 
       <BonusType>BONUS_INCENSE</BonusType> 
    </BonusesRequired> 
    Requires Incense in the plot. And again, setting the variable to NONE will explicitly require no bonus to be present.

    RoutesRequired- the last variable specifying plot conditions. If non-empty, specifies the route types that the plot must have to meet the condition.
    Code:
    <RoutesRequired> 
       <RouteType>ROUTE_ROAD</RouteType> 
       <RouteType>ROUTE_RAILROAD</RouteType> 
    </RoutesRequired> 
    The above requires either a road or a railroad in the plot.

    ReligionsRequired- if non-empty, specifies the religions required for the trigger to activate. For triggers that affect a city, the religions must be present in the city. For triggers not affecting a city, the religion must be present anywhere in the civilization. Hence:
    Code:
    <ReligionsRequired> 
       <ReligionType>RELIGION_BUDDHISM</ReligionType> 
       <ReligionType>RELIGION_CONFUCIANISM</ReligionType> 
       <ReligionType>RELIGION_TAOISM</ReligionType> 
    </ReligionsRequired> 
    That code would check for the presence of Buddhism, Confucianism and Taoism. It&#8217;s used in conjunction with the next variable, see below.

    iNumReligions- is a number. Specifies how many of the religions listed in <ReligionsRequired> must be present for the trigger to activate. If <ReligionsRequired> has been left empty, then any religions will do. For example, for a trigger that affects a city, if <ReligionsRequired> is empty and <iNumReligions> is 2, then cities with at least different 2 religions in them will be eligible for the trigger.

    CorporationsRequired- if non-empty, specifies the corporations required for the trigger to activate. For triggers that affect a city, the religions must be present in the city. For triggers not affecting a city, the religion must be present anywhere in the civilization.
    Code:
    <CorporationsRequired> 
       <CorporationType>CORPORATION_1</CorporationType> 
    <CorporationsRequired> 
    That would require the first corporation, which is Cereal Mills. Just like <ReligionsRequired>, this is used in combination with the next variable.

    iNumCorporations- is a number. Works exactly like <iNumReligions>, but for corporations instead of religions. Specifies how many of the corporations listed in <CorporationsRequired> must be present for the trigger to activate. If <CorporationsRequired> has been left empty, then any corporation will do.

    bPickReligion- can be either 0 or 1. If set to 1, then the trigger will pick a religion for the events that it activates. Which religion is picked can be further specified by the next variables.

    bStateReligion- can be either 0 or 1. If set to 1 and <bPickReligion> is 1, then the affected player&#8217;s state religion will be picked.

    bHolyCity- can be either 0 or 1. If set to 1 and <bPickReligion> is 1, then the player must own the religion&#8217;s holy city in order for it to be picked. If the trigger also picks a city, then the picked city must be the religion&#8217;s holy city. For example, if <bPickReligion>, <bStateReligion>, <bHolyCity> are all 1 and the trigger picks a city (<bPickCity> is also 1), then the city picked will be the holy city of the player&#8217;s state religion, provided he owns that city.

    bPickCorporation- can be either 0 or 1. If set to 1, then a corporation will be picked by the trigger. The corporation must be present in the player&#8217;s civilization to be picked.

    bHeadquarters- can be either 0 or 1. If set to 1 and <bPickCorporation> is 1 then the player must own the corporation&#8217;s headquarters in order for it to be picked. If the trigger also picks a city, then the picked city must be the corporation headquarters. Works like <bHolyCity> does for religions.

    Events- this, ladies and gentlemen, is the holy grail of the trigger XML file. Here event identifiers from Civ4EventInfos.xml are listed that denote the events triggered by this particular trigger. Note that each choice is also technically an event &#8211; therefore, for events that have choices, multiple identifiers will be listed. Take a look at the slave revolt event example:
    Code:
    <Events> 
       <Event>EVENT_SLAVE_REVOLT_1</Event> 
       <Event>EVENT_SLAVE_REVOLT_2</Event> 
       <Event>EVENT_SLAVE_REVOLT_3</Event> 
    </Events> 
    The code means that, when the trigger activates, the player will be allowed to choose between three events. So in-game, a popup with three options will appear. The effects of every choice are, of course, specified in Civ4EventInfos.xml. Triggers that do not have a choice associated with them simply list one event:
    Code:
    <Events> 
       <Event>EVENT_TORNADO_1</Event> 
    <Events> 
    Here the player will have no choice but to accept what happened.

    PrereqEvents- another fun field, this lists the events that must have already happened for this trigger to possibly activate. There are two main uses. One is to simply specify conditional triggers. For example, EVENTTRIGGER_DUSTBOWL_CONT has
    Code:
    <PrereqEvents> 
       <Event>EVENT_DUSTBOWL_2</Event> 
    </PrereqEvents> 
    That means that the trigger can only activate if the EVENT_DUSTBOWL_2 event has occurred previously.
    The second use is for quest done triggers. When creating quests, each quest needs a trigger that checks whether its conditions have been fulfilled. However, a quest can only be completed if it&#8217;s been given. Therefore, for example, EVENTTRIGGER_HORSE_WHISPERING_DONE, which fires when you complete the Horse Whispering quest, has:
    Code:
    <PrereqEvents> 
       <Event>EVENT_HORSE_WHISPERING_1</Event> 
    </PrereqEvents> 
    That code makes sure that the trigger only activates if the EVENT_HORSE_WHISPERING_1 event has occurred, that being the event which assigns the Horse Whispering quest.

    bPrereqEventPlot- can be either 0 or 1. For triggers that have PrereqEvents specified, if set to 1, means that the trigger will fire on the same plot where the prerequisite events fired. This is used with triggers such as continuing slave revolts or dustbowls &#8211; if the prerequisite event has fired, dustbowl or revolts will, if triggered, happen again in the same place where they did before.

    OrPreReqs- if non-empty, lists technologies that player must have for the trigger to activate. These requirements are Or requirements &#8211; that is, only one technology of the listed ones is required. Example:
    Code:
    <OrPreReqs> 
       <PrereqTech>TECH_STEAM_POWER</PrereqTech> 
       <PrereqTech>TECH_STEEL</PrereqTech> 
       <PrereqTech>TECH_SCIENTIFIC_METHOD</PrereqTech> 
       <PrereqTech>TECH_ARTILLERY</PrereqTech> 
    </OrPreReqs> 
    That allows the trigger to activate if the player has Steam Power or Steel or Scientific Method or Artillery.

    AndPreReqs- if non-empty, lists technologies that the player must have for the trigger to activate. These are And requirements &#8211; the player must have all the listed technologies for the trigger. Example:
    Code:
    <AndPreReqs> 
       <PrereqTech>TECH_RIFLING</PrereqTech> 
       <PrereqTech>TECH_STEEL</PrereqTech> 
    </AndPreReqs> 
    This trigger can only fire when the player has both Rifling and Steel technologies.

    ObsoleteTechs- if non-empty, lists technologies that obsolete the trigger, making it ineligible to activate. If multiple technologies are listed, having any of those will invalidate the trigger for that player.
    Code:
    <ObsoleteTechs> 
       <ObsoleteTech>TECH_RIFLING</ObsoleteTech> 
       <ObsoleteTech>TECH_RAILROAD</ObsoleteTech> 
       <ObsoleteTech>TECH_ECONOMICS</ObsoleteTech> 
    </ObsoleteTechs> 
    These settings would obsolete the trigger for any player who has Rifling or Railroad or Economics.

    bRecurring- can be either 0 or 1. If set to 1, the trigger can activate multiple times throughout the game for the same player. If set to 0, it can only activate once per player. Note that the <ClearEvents> field in event description can reset events, making the game forget they have occurred.

    bTeam- can be either 0 or 1. If set to 1, the trigger applies to all players on the same team. Not used in any of the events shipping in BtS.

    bGlobal- can be either 0 or 1. If set to 1, applies to all players. That means that, when triggered for one player, it&#8217;s cosidered to have triggered for all players. Combined with <bRecurring> set to 0, it will result in triggers that can only happen once per game.

    bPickPlayer- can be either 0 or 1. If set to 1, then the trigger will pick another player to affect by the event. This is how events that give stuff to other players or affect relations with them are created. The specific player who gets selected can be further narrowed down by the next six tags.

    bOtherPlayerWar- can be either 0 or 1. If set to 1, then the trigger can activate when the affected player and the other player are at war. When set to 1, the trigger activates for player pairs at peace.

    bOtherPlayerHasReligion- can be either 0 or 1. If set to 1, then the other player must have the religion picked by the trigger (see <bPickReligion>). If <bStateReligion> is 0, then the other player must have the religion somewhere in his empire. If <bStateReligion> is 1, then the other player must have the same state religion.

    bOtherPlayerHasOtherReligion- can be either 0 or 1. Works opposite to the previous variable. If set to 1, the other player must have a different religion than the picked one. If <bStateReligion> is 1, then the other player&#8217;s state religion must be different.

    bOtherPlayerAI- can be either 0 or 1. If set to 1, then the other player must be an AI player.

    iOtherPlayerShareBorders- is a number. Specifies the minimum amount of land plots that the affected player and the other player must have along the border (adjacent to one another).

    OtherPlayerHasTech- is a technology identifier, NONE for no technology. If used, specifies the technology that the other player must have for the trigger to activate.

    bPickCity- can be either 0 or 1. If set to 1, then the trigger will pick a city.

    bPickOtherPlayerCity- can be either 0 or 1. If set to 1, then the trigger will pick one of the other player&#8217;s cities.

    bShowPlot- can be either 0 or 1. If set to 1, then the plot in which the event happens will be visually highlighted.

    iCityFoodWeight- is a number. Acts as a probability multiplier for the food stored in a city. That is, for triggers that pick a city, cities with more food stored will have a higher probability of being picked as iCityFoodWeight increases. 1 is a sane value to prioritize cities with a lot of food stored.

    PythonCanDo- if specified, it&#8217;s the name of the Python function which is executed to check whether the trigger can be triggered. See the section on Python for more information.

    PythonCanDoCity- if specified, it&#8217;s the name of the Python function which checks whether a city is eligible to be picked by the trigger. See the section on Python for more information.

    PythonCanDoUnit- if specified, it&#8217;s the name of the Python function which checks whether a unit is eligible to be picked by the trigger. See the section on Python for more information.

    PythonCallback- if specified, it&#8217;s the name of the Python function which is called when the trigger activates. Not used in any of the BtS events. Don&#8217;t confuse with <PythonCallback> in event descriptions in Civ4EventInfos.xml, which is the function which gets called when the event happens. See the section on Python for more information.
     
  3. Solver

    Solver Civ4/5 beta tester

    Joined:
    Mar 22, 2002
    Messages:
    1,260
    Location:
    Latvia, Riga
    Python and Events


    XML tags allow a good range of possibilities for modding events and their triggers, but some of the more complicated stuff you might want to do requires the use of Python. Python modding, of course, requires a working knowledge of Python, so if you do not have that, feel free to skip this section. This article won&#8217;t teach you Python or how to use Python with Civ4 general. The Python file for event functions is Assets\Python\EntryPoints\CvRandomEventInterface.py.

    Let us first look at how Python can be used in conjuncton with the events described in Civ4EventInfos.xml. As mentioned, each event entry has four Python function tags. If these are filled, then the aproppriate Python functions are called and used in conjunction with whatever XML data has been specified. Here are, again, the tags.

    PythonCallback- that&#8217;s the Python function which is called when the event actually happens. It&#8217;s used to give the event effects more complicated than are possible through raw XML. Quests are the best example of that, as their rewards are usually somewhat complicated.

    Callback functions don&#8217;t need to return a value.

    PythonExpireCheck- that&#8217;s the Python function which is called to see whether the event has expired. It will be called every turn for every event that has occurred. If the event has expired, it gets reset. The practical use of that is for quests. Events that set a quest should sent a Python expiration check function, which will determine the failing conditions of that quest.

    Using Python expiration functions will not let you undo regular (non-quest) events. So if you have an event that, say, adds a promotion to all melee units, and add an expiration check to that event, it won&#8217;t remove the promotion when the expiration function returns true.

    Expiration functions should return a true value for when the event has expired and a false value for when it hasn&#8217;t expired.

    It&#8217;s important to note that expiration functions are only required if the expiration condition can not be described by XML alone. If the only expiration condition you want is, for example, a technology, then you can make the event expire by setting <ObsoleteTechs> in Civ4EventTriggerInfos.xml. That&#8217;s the general idea of event Python functions &#8211; they complement the XML when it can&#8217;t describe what you want.

    PythonCanDo- that&#8217;s the function which gets called to see if the event can be applied/selected. Again, that is required for non-trivial conditions. If the XML specifies, for example, that the event will subtract 100 gold from the treasury, people won&#8217;t be able to choose it unless they have 100 gold. But sometimes you will want to have non-trivial codition checks.

    If the event can happen, the Python function should return a true value, and a false value if it can&#8217;t happen.

    PythonHelp- this function will generate the help text for the event, if required. That text is displayed in mouseovers. This is primarily required for events whose text can change somewhat, such as having a different number depending on the circumstances. For example, if your event gives the player a free unit per every city, then the event&#8217;s help text should be modified according to the number of cities the player has. The function should return a text string.

    Now, let&#8217;s look at some examples of these Python functions in use. The examples are from events that ship with BtS. First, you&#8217;ll notice that functions begin with retrieving data from the argsList passed to them.
    Code:
    def doWeddingFeud2(argsList): 
       iEvent = argsList[0] 
       kTriggeredData = argsList[1] 
    The first thing you should notice is the argument list that applies to event Python functions. For callback functions (<PythonCallback>), expiration (<PythonExpireCheck>) and help functions (<PythonHelp>) argsList[0] will always contain the event identifier number, and argsList[1] will have an object describing the trigger data. For condition functions (<PythonCanDo>) argsList[0] will be the trigger object data. The type of that object is EventTriggeredData and it contains information about the data that the event was triggered with. You can get the affected player, the other player, the picked city and other things from the object.

    EventTriggeredData

    Here&#8217;s what you can do with the object in Python. Assume you have an assignment like
    Code:
    kTriggeredData = argsList[0] 
    Now, there&#8217;s a bunch of useful stuff to do with the object through its member variables.

    kTriggeredData.ePlayer- will return the player ID for whom the event was triggered. To get a CyPlayer object out of it in Python,
    Code:
    player = gc.getPlayer(kTriggeredData.ePlayer) 
    kTriggeredData.eTrigger&#8211; will return the trigger type ID of the trigger for this event. That corresponds to an entry in Civ4EventTriggerInfos.xml
    Code:
    trigger = gc.getEventTriggerInfo(kTriggeredData.eTrigger) 
    will return a trigger info object if you need one.

    kTriggeredData.iTurn- will return the turn number on which the event was triggered. Useful for expiration checks &#8211; you can, for example, easily check how many turns it&#8217;s been since the event was triggered.

    kTriggeredData.iCityId&#8211; will return the ID of the city that&#8217;s been picked by the event. Remember that city IDs are per-player and not global. Therefore, to get a CyCity object in Python, you would
    Code:
    player = gc.getPlayer(kTriggeredData.ePlayer) 
    city = player.getCity(kTriggeredData.iCityId)
    kTriggeredData.iPlotX&#8211; will return the X coordinate of the plot picked by the event.

    kTriggeredData.iPlotY&#8211; will return the Y coordinate of the plot picked by the event.

    If you want to access a CyPlot object for the event&#8217;s plot, do so by
    Code:
    plot = gc.getMap().plot(kTriggeredData.iPlotX,  kTriggeredData.iPlotY) 
    kTriggeredData.iUnitId&#8211; will return the ID of the unit that&#8217;s been picked by the event. Just like city IDs, unit IDs are per-player and not global. So, if you want to get a CyUnit object corresponding to the picked unit, do:
    Code:
    player = gc.getPlayer(kTriggeredData.ePlayer) 
    unit = player.getUnit(kTriggeredData.iUnitId) 
    kTriggeredData.eOtherPlayer&#8211; returns the ID of the other player for events that pick two player. Thus:
    Code:
    player = gc.getPlayer(kTriggeredData.ePlayer) 
    otherPlayer = gc.getPlayer(kTriggeredData.eOtherPlayer) 
    will make player the affected player and otherPlayer the other player, ready for manipulation through Python.

    kTriggeredData.iOtherPlayerCityId&#8211; will return the ID of the other player&#8217;s city that&#8217;s been picked by the event. To get a CyCity object:
    Code:
    otherPlayer = gc.getPlayer(kTriggeredData.eOtherPlayer) 
    otherCity = otherPlayer.getCity(kTriggeredData.iOtherPlayerCityId) 
    kTriggeredData.eReligion&#8211; returns the ID of the religion picked by the event.

    kTriggeredData.eCorporation&#8211; returns the ID of the corporation that has been picked by the event.

    kTriggeredData.eBuilding&#8211; returns the ID of the building that has been picked. A CyBuildingInfo object can be obtained:
    Code:
    buildingInfo = gc.getBuildingInfo(kTriggeredData.eBuilding) 
    The use of these properties enables you to access the information you need about the event and the way it&#8217;s been triggered. Now, a full example of a Python function that executes an event:
    Code:
    def doWeddingFeud2(argsList): 
       iEvent = argsList[0] 
       kTriggeredData = argsList[1] 
        
       player = gc.getPlayer(kTriggeredData.ePlayer) 
       (loopCity, iter) = player.firstCity(false) 
    
       while(loopCity): 
          if loopCity.isHasReligion(kTriggeredData.eReligion): 
             loopCity.changeHappinessTimer(30) 
          (loopCity, iter) = player.nextCity(iter, false) 
           
       return 1
    This is a fairly straightforward function. It iterates through all cities of the affected player and gives temporary happiness to them if they have the religion that&#8217;s been picked by the event trigger. The effect is not particularly complex, but it is something that could not be accomplished purely through XML, so it&#8217;s a good example of why Python is useful in event modding.

    Here&#8217;s an example of a PythonCanDo function, which checks whether the event can be selected.
    Code:
    def canDoWeddingFeud3(argsList): 
       iEvent = argsList[0] 
       kTriggeredData = argsList[1] 
    
       player = gc.getPlayer(kTriggeredData.ePlayer) 
        
       if player.getGold() - 10 * player.getNumCities() < 0: 
          return false 
           
       return true 
    It checks whether the affected player has at least 10 gold per each city he owns. If not, the function returns false and the event can&#8217;t happen, otherwise it returns true and allows the event.

    A good example of the help-text creating functions comes with the Horse Whispering quest. One of the possible rewards gives you free Horse Archers. Their number is variable, though, meaning it couldn&#8217;t exist in the help text. Let&#8217;s look at the help text string in BtS text files:
    Code:
    <Tag>TXT_KEY_EVENT_HORSE_WHISPERING_DONE_HELP_1</Tag> 
    <English>[ICON_BULLET]Receive &#37;d1 [COLOR_UNIT_TEXT]Horse Archers[COLOR_REVERT].</English> 
    As you can see, there&#8217;s a %d1 in the text, which will be substituted by the number. That&#8217;s what the getHelpHorseWhisperingDone1 function does in Python.
    Code:
    def getHelpHorseWhisperingDone1(argsList): 
       iEvent = argsList[0] 
       kTriggeredData = argsList[1] 
        
       map = gc.getMap() 
        
       iNumUnits = gc.getWorldInfo(map.getWorldSize()).getDefaultPlayers() 
       szHelp = localText.getText("TXT_KEY_EVENT_HORSE_WHISPERING_DONE_HELP_1", (iNumUnits, )) 
    
       return szHelp 
    Here, iNumUnits is set to be the default number of players for the current map size and then the number is inserted into the help text string.

    Now, an example of an expiration function. Let&#8217;s look at the function which checks whether the Master Blacksmith quest has expired. The quest picks a city and expires (becomes impossible to complete) if the city which was picked changes hands or is destroyed.
    Code:
    def expireMasterBlacksmith1(argsList): 
       iEvent = argsList[0] 
       kTriggeredData = argsList[1] 
       player = gc.getPlayer(kTriggeredData.ePlayer) 
       city = player.getCity(kTriggeredData.iCityId)    
       if city == None or city.getOwner() != kTriggeredData.ePlayer: 
          return true 
                 
       return false 
    Remember, kTriggeredData.ePlayer will always contain the player for whom the event starting the quest was triggered &#8211; and kTriggeredData.iCityId will always contain the city ID. The function checks if the city is None (which happens if it&#8217;s destroyed) or if the city&#8217;s owner isn&#8217;t the player to whom the quest was given. In these cases, the function returns true, expiring the event.

    Finally, about Python functions that pertain to triggers. Civ4EventTriggers.xml also has Python function tags.

    PythonCanDo- acts just like the same tag for events. Checks whether the trigger can be activated.

    PythonCanDoCity- checks whether a city is eligible to be picked by the trigger.

    PythonCanDoUnit- checks whether a unit is eligible to be picked by the trigger.

    PythonCallback- acts just like the same tag for events. Gets called if the trigger is fired.

    For PythonCanDoCity functions, argsList[0] is the trigger ID, argsList[1] is the player and argsList[2] is the city ID. For PythonCanDoUnit functions, it&#8217;s the same with the exception that argsList[2] is the unit ID. Here&#8217;s an example of a PythonCanDoUnit function for the Champion Unit trigger.
    Code:
    def canTriggerChampionUnit(argsList): 
       eTrigger = argsList[0] 
       ePlayer = argsList[1] 
       iUnit = argsList[2] 
        
       player = gc.getPlayer(ePlayer) 
       unit = player.getUnit(iUnit) 
        
       if unit.isNone(): 
          return false 
           
       if unit.getDamage() > 0: 
          return false 
           
       if unit.getExperience() < 3: 
          return false 
    
       iLeadership = CvUtil.findInfoTypeNum(gc.getPromotionInfo,gc.getNumPromotionInfos(),'PROMOTION_LEADERSHIP') 
       if unit.isHasPromotion(iLeadership): 
          return false 
    
       return true 
    After finding the unit, the function checks if it&#8217;s damaged or has less than 3 experience &#8211; in these cases it returns false. If the unit had the Leadership promotion, it also returns false. If the function returns false, then the unit is not eligible to be picked by the trigger and another one will be found.

    That summarizes how events in BtS interact with Python and what the different function types are. Following are examples of some events you can create via modding.
     
  4. Solver

    Solver Civ4/5 beta tester

    Joined:
    Mar 22, 2002
    Messages:
    1,260
    Location:
    Latvia, Riga
    Creating Events

    Well, so creating events is what you probably want to do if you&#8217;re reading this guide. In this article, I will show you how to create a few example events, explaining along the way. Without further ado, let&#8217;s get straight to it.

    Creating an event (assuming XML modification only) will consist of three main parts. One is the creation of a new trigger, which will outline the conditions, second is creation of the event entry itself, which outlines the effects, and then there is adding text to the proper XML file.

    The Civilization event:

    A somewhat tongue-in-cheek event, a new version of Civilization gets released. You will have three options to deal with it. First, &#8220;Let them play!&#8221; &#8211; boosts happiness in all your cities. Second, &#8220;Use this game in education&#8221; &#8211; will boost the research output of your Universities. Third, &#8220;Sell the franchise abroad&#8221; &#8211; will give you a considerable sum of gold.

    So, first we&#8217;re going to be creating a new trigger. In Civ4EventTriggerInfos.xml, I create an EVENTTRIGGER_CIV_GAME entry, the code for which follows below.
    Code:
          <EventTriggerInfo> 
             <Type>EVENTTRIGGER_CIV_GAME</Type> 
             <WorldNewsTexts> 
                <Text>TXT_KEY_EVENTTRIGGER_CIV_GAME</Text> 
             </WorldNewsTexts> 
             <TriggerTexts> 
                <TriggerText> 
                   <Text>TXT_KEY_EVENT_TRIGGER_CIV_GAME</Text> 
                   <Era>NONE</Era> 
                </TriggerText> 
             </TriggerTexts> 
             <bSinglePlayer>0</bSinglePlayer> 
             <iPercentGamesActive>80</iPercentGamesActive> 
             <iWeight>100</iWeight> 
             <bProbabilityUnitMultiply>0</bProbabilityUnitMultiply> 
             <bProbabilityBuildingMultiply>0</bProbabilityBuildingMultiply> 
             <Civic>NONE</Civic> 
             <iMinTreasury>0</iMinTreasury> 
             <iMinPopulation>0</iMinPopulation> 
             <iMaxPopulation>0</iMaxPopulation> 
             <iMinMapLandmass>0</iMinMapLandmass> 
             <iMinOurLandmass>0</iMinOurLandmass> 
             <iMaxOurLandmass>-1</iMaxOurLandmass> 
             <MinDifficulty>NONE</MinDifficulty> 
             <iAngry>0</iAngry> 
             <iUnhealthy>0</iUnhealthy> 
             <UnitsRequired/> 
             <iNumUnits>0</iNumUnits> 
             <iNumUnitsGlobal>0</iNumUnitsGlobal> 
             <iUnitDamagedWeight>0</iUnitDamagedWeight> 
             <iUnitDistanceWeight>0</iUnitDistanceWeight> 
             <iUnitExperienceWeight>0</iUnitExperienceWeight> 
             <bUnitsOnPlot>0</bUnitsOnPlot> 
             <BuildingsRequired/> 
             <iNumBuildings>0</iNumBuildings> 
             <iNumBuildingsGlobal>0</iNumBuildingsGlobal> 
             <iNumPlotsRequired>0</iNumPlotsRequired> 
             <bOwnPlot>0</bOwnPlot> 
             <iPlotType>-1</iPlotType> 
             <FeaturesRequired/> 
             <TerrainsRequired/> 
             <ImprovementsRequired/> 
             <BonusesRequired/> 
             <RoutesRequired/> 
             <ReligionsRequired/> 
             <iNumReligions>0</iNumReligions> 
             <CorporationsRequired/> 
             <iNumCorporations>0</iNumCorporations> 
             <bPickReligion>0</bPickReligion> 
             <bStateReligion>0</bStateReligion> 
             <bHolyCity>0</bHolyCity> 
             <bPickCorporation>0</bPickCorporation> 
             <bHeadquarters>0</bHeadquarters> 
             <Events> 
                <Event>EVENT_CIV_GAME_1</Event> 
                <Event>EVENT_CIV_GAME_2</Event> 
                <Event>EVENT_CIV_GAME_3</Event> 
             </Events> 
             <PrereqEvents/> 
             <bPrereqEventPlot>0</bPrereqEventPlot> 
             <OrPreReqs> 
                <PrereqTech>TECH_COMPUTERS</PrereqTech> 
             </OrPreReqs> 
             <AndPreReqs/> 
             <ObsoleteTechs/> 
             <bRecurring>0</bRecurring> 
             <bTeam>0</bTeam> 
             <bGlobal>0</bGlobal> 
             <bPickPlayer>0</bPickPlayer> 
             <bOtherPlayerWar>0</bOtherPlayerWar> 
             <bOtherPlayerHasReligion>0</bOtherPlayerHasReligion> 
             <bOtherPlayerHasOtherReligion>0</bOtherPlayerHasOtherReligion> 
             <bOtherPlayerAI>0</bOtherPlayerAI> 
             <iOtherPlayerShareBorders>0</iOtherPlayerShareBorders> 
             <OtherPlayerHasTech>NONE</OtherPlayerHasTech> 
             <bPickCity>0</bPickCity> 
             <bPickOtherPlayerCity>0</bPickOtherPlayerCity> 
             <bShowPlot>0</bShowPlot> 
             <iCityFoodWeight>0</iCityFoodWeight> 
             <PythonCanDo/> 
             <PythonCanDoCity/> 
             <PythonCanDoUnit/> 
             <PythonCallback/> 
          </EventTriggerInfo> 
    Almost all tags are at their default values, which means no special conditions. The interesting part is, of course, the <Events> tag, which specifies that the trigger, when activated, will give choices between those 3 events &#8211; we will create them next. <OrPreReqs> specifies that the Computers tech is required for the trigger, which makes sense given the event&#8217;s context. Nothing fancy is added &#8211; we don&#8217;t need to pick specific players, cities, plots, check for population, religion or anything like that.

    Now, I go into the Civ4EventInfos.xml file and create three new events. The first one is as follows.
    Code:
    <EventInfo> 
             <Type>EVENT_CIV_GAME_1</Type> 
             <Description>TXT_KEY_EVENT_CIV_GAME_1</Description> 
             <LocalInfoText/> 
             <WorldNewsTexts/> 
             <OtherPlayerPopup/> 
             <QuestFailText/> 
             <bQuest>0</bQuest> 
             <bGlobal>0</bGlobal> 
             <bTeam>0</bTeam> 
             <bPickCity>0</bPickCity> 
             <bPickOtherPlayerCity>0</bPickOtherPlayerCity> 
             <bDeclareWar>0</bDeclareWar> 
             <iGold>0</iGold> 
             <bGoldToPlayer>0</bGoldToPlayer> 
             <iRandomGold>0</iRandomGold> 
             <iCulture>0</iCulture> 
             <iEspionagePoints>0</iEspionagePoints> 
             <bGoldenAge>0</bGoldenAge> 
             <iFreeUnitSupport>0</iFreeUnitSupport> 
             <iInflationMod>0</iInflationMod> 
             <iSpaceProductionMod>0</iSpaceProductionMod> 
             <Tech>NONE</Tech> 
             <TechFlavors/> 
             <iTechPercent>0</iTechPercent> 
             <iTechCostPercent>0</iTechCostPercent> 
             <iTechMinTurnsLeft>0</iTechMinTurnsLeft> 
             <PrereqTech>NONE</PrereqTech> 
             <UnitClass>NONE</UnitClass> 
             <iNumFreeUnits>0</iNumFreeUnits> 
             <bDisbandUnit>0</bDisbandUnit> 
             <iUnitExperience>0</iUnitExperience> 
             <iUnitImmobileTurns>0</iUnitImmobileTurns> 
             <UnitPromotion/> 
             <UnitName/> 
             <UnitCombatPromotions/> 
             <UnitClassPromotions/> 
             <BuildingClass>NONE</BuildingClass> 
             <iBuildingChange>0</iBuildingChange> 
             <BuildingExtraYields/> 
             <BuildingExtraCommerces/> 
             <BuildingExtraHappies/> 
             <BuildingExtraHealths/> 
             <iHappy>1</iHappy> 
             <iHealth>0</iHealth> 
             <iHurryAnger>0</iHurryAnger> 
             <iHappyTurns>0</iHappyTurns> 
             <iRevoltTurns>0</iRevoltTurns> 
             <iMinPillage>0</iMinPillage> 
             <iMaxPillage>0</iMaxPillage> 
             <iFood>0</iFood> 
             <iFoodPercent>0</iFoodPercent> 
             <FreeSpecialistCounts/> 
             <FeatureType>NONE</FeatureType> 
             <iFeatureChange>0</iFeatureChange> 
             <ImprovementType>NONE</ImprovementType> 
             <iImprovementChange>0</iImprovementChange> 
             <BonusType>NONE</BonusType> 
             <iBonusChange>0</iBonusChange> 
             <RouteType>NONE</RouteType> 
             <iRouteChange>0</iRouteChange> 
             <BonusRevealed>NONE</BonusRevealed> 
             <BonusGift>NONE</BonusGift> 
             <PlotExtraYields/> 
             <iConvertOwnCities>0</iConvertOwnCities> 
             <iConvertOtherCities>0</iConvertOtherCities> 
             <iMaxNumReligions>-1</iMaxNumReligions> 
             <iOurAttitudeModifier>0</iOurAttitudeModifier> 
             <iAttitudeModifier>0</iAttitudeModifier> 
             <iTheirEnemyAttitudeModifier>0</iTheirEnemyAttitudeModifier> 
             <iPopulationChange>0</iPopulationChange> 
             <AdditionalEvents/> 
             <EventTimes/> 
             <ClearEvents/> 
             <PythonCallback/> 
             <PythonExpireCheck/> 
             <PythonCanDo/> 
             <PythonHelp/> 
             <Button>,Art/Interface/Buttons/Process/Blank.dds,Art/Interface/Buttons/Beyond_the_Sword_Atlas.dds,8,5</Button> 
             <iAIValue>1000</iAIValue> 
          </EventInfo>
    Once again, most tags are at their default values. Other than the name and text description tags, the one that matters is <iHappy>, which is set to 1. That means that we&#8217;ll get +1 happiness in all cities (<bPickCity> being 0).

    For the second choice, I create a second event. In order to waste less screen space, I&#8217;ll only show the relevant tags below.
    Code:
    <Type>EVENT_CIV_GAME_2</Type> 
             <Description>TXT_KEY_EVENT_CIV_GAME_2</Description> 
             <BuildingExtraCommerces> 
                <BuildingExtraCommerce> 
                   <BuildingClass>BUILDINGCLASS_UNIVERSITY</BuildingClass> 
                   <CommerceType>COMMERCE_RESEARCH</CommerceType> 
                   <iExtraCommerce>3</iExtraCommerce> 
                </BuildingExtraCommerce> 
             </BuildingExtraCommerces> 
    Other tags being at their default values, this means that every University will provide 3 extra research beakers.

    And our third event has these tags:
    Code:
    <Type>EVENT_CIV_GAME_3</Type> 
             <Description>TXT_KEY_EVENT_CIV_GAME_3</Description> 
    <iGold>320</iGold> 
             <iRandomGold>80</iRandomGold> 
    That will give a base 320 gold with an extra possible 80 randomly.

    The mechanics of our new event are done! Now, we only need to add text. In Civ4GameText_Events_BTS.xml (in Assets\XML\Text), I add new tags as follows.
    Code:
    <TEXT> 
       <Tag>TXT_KEY_EVENTTRIGGER_CIV_GAME</Tag> 
       <English>The %s1_civ_adjective game designers have developed a new game of Civilization.</English> 
    </TEXT>
    TXT_KEY_EVENTTRIGGER_CIV_GAME is specified as world news text in the trigger file &#8211; that is, everyone will see the above text when the trigger fires.
    Code:
    <TEXT> 
       <Tag>TXT_KEY_EVENT_TRIGGER_CIV_GAME</Tag> 
       <English>Our game designers have developed a new addictive game of Civilization. How shall we proceed?</English> 
    </TEXT> 
    That&#8217;s specified under the TriggerTexts tag, and will be shown in a popup to the player who gets the event. Now, we only need to add the choice descriptions.
    Code:
    <TEXT> 
       <Tag>TXT_KEY_EVENT_CIV_GAME_1</Tag> 
       <English>Send congratulations to the developers. Our people should enjoy this game.</English> 
    </TEXT> 
       <TEXT> 
       <Tag>TXT_KEY_EVENT_CIV_GAME_2</Tag> 
       <English>This game seems to have great educational potential. Suggest that it be used in our educational system.</English> 
    </TEXT> 
    <TEXT> 
       <Tag>TXT_KEY_EVENT_CIV_GAME_3</Tag> 
       <English>Computer games? They have no place in our society! Sell the franchise to foreign buyers!</English> 
    </TEXT> 
    That&#8217;s it, the event is done! It will be in when you star a new game.
    Here&#8217;s a useful hint. You can test the event in-game. There is a function CvPlayer::trigger() which activates a trigger given its ID. I&#8217;ll show you how to activate the event we just created, the general idea is the same for all triggers.

    Start BtS in debug mode (by setting CheatCode=chipotle in the ini file). Start a new game in the Future era. Now, press Shift+~ (tilde) to bring up the in-game Python console. You&#8217;ll need to find out the ID of the trigger you added. To do so, in the console, enter
    Code:
    import CvUtil 
    num = CvUtil.findInfoTypeNum(gc.getEventTriggerInfo, gc.getNumEventTriggerInfos(), &#8216;EVENTTRIGGER_CIV_GAME&#8217;) 
    print num 
    That will output the ID of the Civ Game trigger. It was 176 for me. You don&#8217;t have to enter the print num line, but it&#8217;s a way of checking if you did everything correctly. If it outputs -1, something went wrong. The same result can be achieved by using another function. Instead of the above, you could do
    Code:
    num = gc.getInfoTypeForString(&#8220;EVENTTRIGGER_CIV_GAME&#8221;) 
    print num 
    It&#8217;s essentially the same, but I prefer the first way for reasons I can&#8217;t explain myself. Anyway, when you&#8217;ve obtained the trigger number, you need to trigger it for yourself. In the Python console, do:
    Code:
    p = gc.getPlayer(0) 
    p.trigger(num) 
    In single-player, the player with an ID of 0 is always the human, so the first line will give a reference to you. After the second line, a popup should appear just as if the event happened.

    It&#8217;s important to note that, while the trigger() function can be used to forcefully activate a trigger, it will only work if the requirements are met. That&#8217;s why it was important to start a Future era game to test this addition &#8211; in the Future era, you already start with Computers. If you had started an Ancient era game, nothing would have happened after using the trigger() function.

    The Danthrax event:

    Danthrax told me that he would like a warning displayed the first time a slave revolt occurs, with the actual effects of the slave revolt only occuring subsequently. I&#8217;ll show how to do that, but will only be inserting the relevant tags here again.

    The idea is to make a trigger with the same conditions as slave revolts, and have that trigger produce an event which merely gives a warning. Then, slave revolts are modified to require that warning to have happened. So first, I add a new trigger. I copy the slave revolt trigger and modify it.
    Code:
    <Type>EVENTTRIGGER_SLAVE_REVOLT_WARNING</Type> 
    <WorldNewsTexts/> 
    <TriggerTexts> 
       <TriggerText> 
          <Text>TXT_KEY_EVENT_TRIGGER_SLAVE_REVOLT_WARNING</Text> 
          <Era>NONE</Era> 
       </TriggerText> 
    </TriggerTexts> 
    <Civic>CIVIC_SLAVERY</Civic> 
    <iMinPopulation>4</iMinPopulation> 
    <bOwnPlot>1</bOwnPlot> 
    <Events> 
       <Event>EVENT_SLAVE_REVOLT_WARNING</Event> 
    </Events> 
    <bPickCity>1</bPickCity> 
    <bRecurring>0</bRecurring> 
    <bPickOtherPlayerCity>0</bPickOtherPlayerCity> 
    <bShowPlot>1</bShowPlot> 
    We will not need any text displayed when the trigger activates. The event will show a pop-up message anyway. However, <TriggerTexts> may not be empty. The trigger conditions are just like those of the slave revolt trigger. <bRecurring> is set to 0, though, because we only want one warning. Now I make modifications to the slave revolt trigger.
    Code:
    <Type>EVENTTRIGGER_SLAVE_REVOLT</Type> 
    <iPercentGamesActive>100</iPercentGamesActive> 
    <PrereqEvents> 
       <Event>EVENT_SLAVE_REVOLT_WARNING</Event> 
    </PrereqEvents> 
    I changed iPercentGamesActive to 100 to make the event active in all games. That&#8217;s to avoid situations when the warning appears but no slave revolts an actually happen that game. If the warning event hasn&#8217;t been activated for that particular game, slave revolts won&#8217;t occur anyway.

    Now, it&#8217;s time to add the slave revolt warning event itself.
    Code:
    <Type>EVENT_SLAVE_REVOLT_WARNING</Type> 
    <Description>TXT_KEY_EVENT_SLAVE_REVOLT_WARNING</Description> 
    All tags are at their default values so that the event basically does nothing. It will, though, still display a popup with the description text. So, time for text tag addition.
    Code:
    <TEXT> 
    <Tag>TXT_KEY_EVENT_SLAVE_REVOLT_WARNING</Tag> 
       <English>Sir, our slaves are on the verge of revolting! As long as our society continues to use slaves, there is a possibility of them revolting!</English> 
    </TEXT> 
    <TEXT> 
       <Tag>TXT_KEY_EVENT_TRIGGER_SLAVE_REVOLT_WARNING</Tag> 
       <English>The slaves of %s2_city are on the verge of revolt!</English> 
    </TEXT>
    Now that is done, too - you will get a warning before your first slave revolt.

    Great Beast:

    For the last example, here&#8217;s a Great Beast event. Your hunters see a beast in a hunting camp, and you are presented with choices. The trigger&#8217;s relevant parts are as follows:
    Code:
    <Type>EVENTTRIGGER_GREAT_BEAST</Type> 
    <WorldNewsTexts> 
       <Text>TXT_KEY_EVENTTRIGGER_GREAT_BEAST</Text> 
    </WorldNewsTexts> 
    <TriggerTexts> 
       <TriggerText> 
          <Text>TXT_KEY_EVENT_GREAT_BEAST</Text> 
          <Era>NONE</Era> 
       </TriggerText> 
    </TriggerTexts> 
    <iNumPlotsRequired>1</iNumPlotsRequired> 
    <bOwnPlot>1</bOwnPlot> 
    <ImprovementsRequired> 
       <ImprovementType>IMPROVEMENT_CAMP</ImprovementType> 
    </ImprovementsRequired> 
    <bPickReligion>1</bPickReligion> 
    <bStateReligion>1</bStateReligion> 
    <Events> 
       <Event>EVENT_GREAT_BEAST_1</Event> 
       <Event>EVENT_GREAT_BEAST_2</Event> 
       <Event>EVENT_GREAT_BEAST_3</Event> 
    </Events> 
    <AndPreReqs> 
       <PrereqTech>TECH_HUNTING</PrereqTech> 
       <PrereqTech>TECH_POLYTHEISM</PrereqTech> 
    </AndPreReqs> 
    <ObsoleteTechs> 
       <ObsoleteTech>TECH_EDUCATION</ObsoleteTech> 
    </ObsoleteTechs> 
    <bShowPlot>1</bShowPlot> 
    As you can see, the trigger requires the player to have a plot with a Camp on it. In addition, Hunting and Polytheism are required, as is a state religion. The event won&#8217;t happen after the discovery of education. The first choice is simple &#8211; it merely gives +1 food in the plot.
    Code:
    <Type>EVENT_GREAT_BEAST_1</Type> 
    <Description>TXT_KEY_EVENT_GREAT_BEAST_1</Description> 
    <PlotExtraYields> 
       <PlotExtraYield> 
          <YieldType>YIELD_FOOD</YieldType> 
          <iExtraYield>1</iExtraYield> 
       </PlotExtraYield> 
    </PlotExtraYields> 
    The second choice costs some gold and adds 1 population to a city.
    Code:
    <Type>EVENT_GREAT_BEAST_2</Type> 
    <Description>TXT_KEY_EVENT_GREAT_BEAST_2</Description> 
    <bPickCity>1</bPickCity> 
    <iGold>-15</iGold> 
    <iPopulationChange>1</iPopulationChange> 
    The third choice is more complex, adding 1 happiness temporarily to cities of state religion.
    Code:
    <Type>EVENT_GREAT_BEAST_3</Type> 
    <Description>TXT_KEY_EVENT_GREAT_BEAST_3</Description> 
    <PythonCallback>doGreatBeast3</PythonCallback> 
    <PythonHelp>getHelpGreatBeast3</PythonHelp> 
    The event XML code does nothing special except calling the Python functions.
    Code:
    def doGreatBeast3(argsList): 
       kTriggeredData = argsList[1] 
        
       player = gc.getPlayer(kTriggeredData.ePlayer) 
       (loopCity, iter) = player.firstCity(false) 
    
       while(loopCity): 
          if loopCity.isHasReligion(kTriggeredData.eReligion): 
             loopCity.changeHappinessTimer(25) 
          (loopCity, iter) = player.nextCity(iter, false) 
    This Python function is called when the event is chosen. It checks all the player&#8217;s cities for presence of the state religion (because the religion picked by the trigger will be the state religion), and adds 25 turns to the happiness timer of those cities, which results in +1 happiness for that time.

    Also, the help text needs to be displayed appropriately, so here&#8217;s the PythonHelp function:
    Code:
    def geHelpGreatBeast3(argsList): 
       kTriggeredData = argsList[1] 
       religion = gc.getReligionInfo(kTriggeredData.eReligion) 
    
       szHelp = localText.getText("TXT_KEY_EVENT_GREAT_BEAST_3_HELP", (gc.getDefineINT("TEMP_HAPPY"), 25, religion.getChar())) 
    
       return szHelp 
    That takes the TXT_KEY_EVENT_GREAT_BEAST_3_HELP text tag, adding the global variable TEMP_HAPPY, the number 25 and the symbol of your state religion into it. The text tag itself looks like this:
    Code:
    <TEXT> 
       <Tag>TXT_KEY_EVENT_GREAT_BEAST_3_HELP</Tag> 
    <English>[ICON_BULLET]%D1[ICON_HAPPY] in all cities with %F3_religion for %d2 [NUM2:turn:turns]</English> 
    </TEXT> 
    And finally, here&#8217;s the rest of the text for the event.
    Code:
    <TEXT> 
       <Tag>TXT_KEY_EVENTTRIGGER_GREAT_BEAST</Tag> 
       <English>%s1_civ_adjective hunters have discovered a great beast lurking near one of their camps</English> 
    </TEXT> 
    <TEXT> 
       <Tag>TXT_KEY_EVENT_GREAT_BEAST</Tag> 
       <English>A great beast has been discovered near one of our hunting camps</English> 
    </TEXT> 
    <TEXT> 
       <Tag>TXT_KEY_EVENT_GREAT_BEAST_1</Tag> 
       <English>Let it breed and give offspring. Hunt sparingly.</English> 
    </TEXT> 
    <TEXT> 
       <Tag>TXT_KEY_EVENT_GREAT_BEAST_2</Tag> 
       <English>Looks like it could feed many people. Organize a hunting party!</English> 
    <TEXT> 
    <TEXT> 
       <Tag>TXT_KEY_EVENT_GREAT_BEAST_3</Tag> 
       <English>Hunt it? Are you mad? The beast is a manifestation of a divine spirit!</English> 
    </TEXT> 
    I hope this guide was useful in providing an insight into how to mod events in BtS. Remember that you can also download a small event mod from this thread, which contains the events described in the examples above, as well as a few other tweaks and new events.

    Good luck with your modding, and be bold!
     
  5. Solver

    Solver Civ4/5 beta tester

    Joined:
    Mar 22, 2002
    Messages:
    1,260
    Location:
    Latvia, Riga
    Added links to an example event mod.
     
  6. Gaius Octavius

    Gaius Octavius Chieftain

    Joined:
    Jul 28, 2006
    Messages:
    4,016
    Woodelf time... :mischief:

    Thank you very much for this--it's quite comprehensive. Would that we had such a guide for all our modding needs. :D
     
  7. Ekmek

    Ekmek on steam: ekmek_e

    Joined:
    Aug 7, 2002
    Messages:
    6,045
    Location:
    San Diego, California
    Awesome Solver!

    So I'm assuming your slic experience had a hand in this :)

    I guess I should wait until BTS before I start working on 'Peacekeeping' quests. You know...so and so city with a different religion then the state religion is being oppressed so its now a quest to "help" the city by taking it over with peacekeepers ;)

    But I noticed there isn't anything for identifying if a city is less than 50&#37; of the owning civilization's ethnic group. And I didn't see anything to identify if the owner of the city is different than the founder or if the city has has a different citystyle than owning civilization. this was stuff I added to the ctp2 source code and think would be really cool in BTS. Any way to add those?

    thanks,

    'E'
     
  8. Solver

    Solver Civ4/5 beta tester

    Joined:
    Mar 22, 2002
    Messages:
    1,260
    Location:
    Latvia, Riga
    To be honest, I don't think SLIC experience mattered here. SLIC feels more like a weaker version of C++, event modding is primarily XML and with Python thrown in for more complex stuff. And Python dances circles around SLIC.

    These requests are the sort of a thing you'd do through Python. Identifying if a city is less than 50% owner's culture is trivial to do in Python. I don't remember if there's already a function to get culture as a percentage or not, but even if not, it's just a few lines of code.

    So, absolutely no problem :)
     
  9. Ekmek

    Ekmek on steam: ekmek_e

    Joined:
    Aug 7, 2002
    Messages:
    6,045
    Location:
    San Diego, California
    I didn't see anything on this. But I cant recall if in warlords when a city is captured by a civ if it retains its original citystyle. I thought it does...so is there a way to identify that in python?
     
  10. Solver

    Solver Civ4/5 beta tester

    Joined:
    Mar 22, 2002
    Messages:
    1,260
    Location:
    Latvia, Riga
    I don't remember for sure. If not, it can be done through the SDK and exposed to Python. I think that there are Python functions to retrieve the unit art style but not the civilization art style. If so, you could easily expose the SDK function to Python, though. CvCivilizationInfo::getArtStyleType() retrieves the art style, so you'd just need to create a Python wrapper function and expose it, which takes a couple of minutes in the SDK.
     
  11. woodelf

    woodelf Bard

    Joined:
    Jun 12, 2003
    Messages:
    15,036
    Location:
    Gallery
    Thanks Solver. This is a super write-up.

    I'll get you next time Gaius. :p
     
  12. Kael

    Kael Chieftain

    Joined:
    May 6, 2002
    Messages:
    17,401
    Location:
    Ohio
    Very cool, I added a link to this in the modders guide so interested people can jump over and drop any event specific questions and feedback here.
     
  13. Solver

    Solver Civ4/5 beta tester

    Joined:
    Mar 22, 2002
    Messages:
    1,260
    Location:
    Latvia, Riga
    Thanks Kael!
     
  14. Dom Pedro II

    Dom Pedro II Modder For Life

    Joined:
    Apr 3, 2002
    Messages:
    6,811
    Location:
    Exit 16, New Jersey
    I see a lot of stuff that basically has Y event requiring X, or the probability of Y event happening is changed by X, but what I don't see is anything that basically says: When X happens, trigger Y. For example, I see that a building can be set as a requirement for a trigger to occur, but I don't see anything that says that the construction of said building will cause a particular event to happen.
     
  15. Solver

    Solver Civ4/5 beta tester

    Joined:
    Mar 22, 2002
    Messages:
    1,260
    Location:
    Latvia, Riga
    You do stuff like that by setting a trigger's weight to -1. Then, when the conditions are met, it will be triggered at once.

    Code:
    <EventTriggerInfo> 
             <Type>EVENTTRIGGER_IMMEDIATE</Type> 
             <WorldNewsTexts> 
                <Text>TXT_KEY_EVENTTRIGGER_IMMEDIATE_BLAH</Text> 
             </WorldNewsTexts> 
             <TriggerTexts> 
                <TriggerText> 
                   <Text>TXT_KEY_BLAH</Text> 
                   <Era>NONE</Era> 
                </TriggerText> 
             </TriggerTexts> 
             <bSinglePlayer>0</bSinglePlayer> 
             <iPercentGamesActive100</iPercentGamesActive> 
             <iWeight>-1</iWeight> 
             <bProbabilityUnitMultiply>0</bProbabilityUnitMultiply> 
             <bProbabilityBuildingMultiply>0</bProbabilityBuildingMultiply> 
             <Civic>NONE</Civic> 
             <iMinTreasury>0</iMinTreasury> 
             <iMinPopulation>0</iMinPopulation> 
             <iMaxPopulation>0</iMaxPopulation> 
             <iMinMapLandmass>0</iMinMapLandmass> 
             <iMinOurLandmass>0</iMinOurLandmass> 
             <iMaxOurLandmass>-1</iMaxOurLandmass> 
             <MinDifficulty>NONE</MinDifficulty> 
             <iAngry>0</iAngry> 
             <iUnhealthy>0</iUnhealthy> 
             <UnitsRequired/> 
             <iNumUnits>0</iNumUnits> 
             <iNumUnitsGlobal>0</iNumUnitsGlobal> 
             <iUnitDamagedWeight>0</iUnitDamagedWeight> 
             <iUnitDistanceWeight>0</iUnitDistanceWeight> 
             <iUnitExperienceWeight>0</iUnitExperienceWeight> 
             <bUnitsOnPlot>0</bUnitsOnPlot> 
             <BuildingsRequired/> 
             <iNumBuildings>0</iNumBuildings> 
             <iNumBuildingsGlobal>0</iNumBuildingsGlobal> 
             <iNumPlotsRequired>0</iNumPlotsRequired> 
             <bOwnPlot>0</bOwnPlot> 
             <iPlotType>-1</iPlotType> 
             <FeaturesRequired/> 
             <TerrainsRequired/> 
             <ImprovementsRequired/> 
             <BonusesRequired/> 
             <RoutesRequired/> 
             <ReligionsRequired/> 
             <iNumReligions>0</iNumReligions> 
             <CorporationsRequired/> 
             <iNumCorporations>0</iNumCorporations> 
             <bPickReligion>0</bPickReligion> 
             <bStateReligion>0</bStateReligion> 
             <bHolyCity>0</bHolyCity> 
             <bPickCorporation>0</bPickCorporation> 
             <bHeadquarters>0</bHeadquarters> 
             <Events> 
                <Event>EVENT_BLAHBLAH</Event> 
             </Events> 
             <PrereqEvents/> 
             <bPrereqEventPlot>0</bPrereqEventPlot> 
             <OrPreReqs> 
                <PrereqTech>TECH_GUNPOWDER</PrereqTech> 
             </OrPreReqs> 
             <AndPreReqs/> 
             <ObsoleteTechs/> 
             <bRecurring>0</bRecurring> 
             <bTeam>0</bTeam> 
             <bGlobal>0</bGlobal> 
             <bPickPlayer>0</bPickPlayer> 
             <bOtherPlayerWar>0</bOtherPlayerWar> 
             <bOtherPlayerHasReligion>0</bOtherPlayerHasReligion> 
             <bOtherPlayerHasOtherReligion>0</bOtherPlayerHasOtherReligion> 
             <bOtherPlayerAI>0</bOtherPlayerAI> 
             <iOtherPlayerShareBorders>0</iOtherPlayerShareBorders> 
             <OtherPlayerHasTech>NONE</OtherPlayerHasTech> 
             <bPickCity>0</bPickCity> 
             <bPickOtherPlayerCity>0</bPickOtherPlayerCity> 
             <bShowPlot>0</bShowPlot> 
             <iCityFoodWeight>0</iCityFoodWeight> 
             <PythonCanDo/> 
             <PythonCanDoCity/> 
             <PythonCanDoUnit/> 
             <PythonCallback/> 
          </EventTriggerInfo>
    
    The above trigger will activate as soon as you get Gunpowder, no matter what.
     
  16. Ball Lightning

    Ball Lightning www.sporedum.net

    Joined:
    Apr 2, 2006
    Messages:
    2,126
    Location:
    Thunderstorm, Melbourne, Australia
  17. Ekmek

    Ekmek on steam: ekmek_e

    Joined:
    Aug 7, 2002
    Messages:
    6,045
    Location:
    San Diego, California
    ok another event question. Is it possible to hav events happen more likely to the most powerful player? Especially in cases where the human is dominating the game?

    thanks for entertaining my questions :)
     
  18. Solver

    Solver Civ4/5 beta tester

    Joined:
    Mar 22, 2002
    Messages:
    1,260
    Location:
    Latvia, Riga
    That is something I hadn't imagined before, but you could do by using Python functions to calculate power. There are useful functions such as getPower(), getTotalLand() and such. To have a trigger that's only possible for the player with the strongest military, I would do something like:

    Code:
    def canDoSomeEvent (self):
          kTriggeredData = argsList[1]
          player = gc.getPlayer(kTriggeredData.ePlayer)
          fPower = player.getPower()
          for iLoopPlayer in range(gc.getMAX_PLAYERS()):
                 if (gc.getPlayer(iLoopPlayer).isAlive() and not gc.getPlayer(iLoopPlayer).isBarbarian()):
                         if (gc.getPlayer(iLoopPlayer).getPower() > fPower):
                                  return false
          return true
    
    I haven't verified if it runs, as I just made that code up on the spot, but that or something very similar should work.
     
  19. Ekmek

    Ekmek on steam: ekmek_e

    Joined:
    Aug 7, 2002
    Messages:
    6,045
    Location:
    San Diego, California
    Thanks Solver.

    I'm thinking of adding an event (once I get BtS) for Holy Wars (different than the AP). Kind of a mix between an event and quest. Something like:

    So how do I find the most cultured civ?

    How do I calculate by turn 30 if the civ is most cultured?

    What do I use to make the extremist unis only available with those civics and during this 30 turn window?

    Thanks
     
  20. Solver

    Solver Civ4/5 beta tester

    Joined:
    Mar 22, 2002
    Messages:
    1,260
    Location:
    Latvia, Riga
    That's a pretty complex one. Finding the most cultured civ isn't a problem, just loop through all civs. It'd be similar to the code in my previous post. To reset the 30 turn window later, you just create another event that triggers 30 turns later and has the opposite effects. I'm not immediately sure how to enable special units, though.
     

Share This Page