{BtS Mod} Events of the World

Events in Python

Spoiler :
15.3 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’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’s the Python function which is called when the event actually happens. It’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’t need to return a value.

PythonExpireCheck- that’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’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’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.

15.4 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.

 
Creating Events

Spoiler :
15.5 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 &#37;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 at [insert link], 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!

 
Well... it looks like much of my hard work was for nothing... Oh well. I created the "unofficial" events expansion which is very similar to what it looks like they have made possible. See the links in my signature if you are curious.

Event so, I will probably be merging my mod with theirs for a BtS release which will take care of many of these requests that have been made
 
I wouldn't say it's for nothing. You can still port it to Warlords, right? That's no small achievement.
 
don't feel bad, im sure many people will find use for your mod.
 
Well... it looks like much of my hard work was for nothing... Oh well. I created the "unofficial" events expansion which is very similar to what it looks like they have made possible. See the links in my signature if you are curious.

Event so, I will probably be merging my mod with theirs for a BtS release which will take care of many of these requests that have been made

Some people will not be able to get BtS so your mod will be great for them, but you can also help to attempt to make the biggest Events mod for BtS if you want.
 
Impaler[WrG];5594721 said:
Theirs also a Modular xml Loading system *wink* so you can create separate little packages for each Event and let people mix and match them as they like, it also makes for faster/easier downloading.

Can you explain this to me?
 
It means you don't have to re-do the entire XML file; you just create your own additions and plop it down in the mod directory, and the game will add it to the existing events. Correct?
 
Basically, it allows you to create small XML files for individual additions.

Read the section on Modular XML in the Modder's Guide Kael posted if you want to know about it in details.
 
heres another random event that just popped in my mind (sounds Disneylike, i think):

your ruler has, while inspecting [city], discovered a poor boy who miraculously has amazing administrative capabilities (i.e., he is unnaturally smart). your ruler wishes to adopt the boy and make him governor of [city], but his ministers are not very happy about the prospect.

- Screw the ministers, adopt the boy! (-25&#37; gold in capital from ministers being more corrupt because they hate you, but... +25% gold and production in [city])

- Ah, forget the kid. (nothing)
 
heres another random event that just popped in my mind (sounds Star Wars like, i think):

your ruler has, while inspecting [city], discovered a poor boy who miraculously has amazing force potential (i.e., he is the chosen one). your ruler wishes to adopt the boy and make him his apprentice, but the Jedi Council not very happy about the prospect.

- Screw the Jedi Council, adopt the boy! (-25% gold in capital from ministers being more corrupt because they hate you, but... +25% gold and production in [city])

- Ah, forget the kid. (nothing)

fixed:goodjob:
 
heres another random event that just popped in my mind (sounds Disneylike, i think):

your ruler has, while inspecting [city], discovered a poor boy who miraculously has amazing administrative capabilities (i.e., he is unnaturally smart). your ruler wishes to adopt the boy and make him governor of [city], but his ministers are not very happy about the prospect.

- Screw the ministers, adopt the boy! (-25% gold in capital from ministers being more corrupt because they hate you, but... +25% gold and production in [city])

- Ah, forget the kid. (nothing)


Good one. its added.
 
excellent... excellent...

hers another random event:

(only when under war)

A war protest in [city] is starting to spiral out of control.

- Talk with the kids to calm them down. (-300 gold, +1 :mad:)
- Take them out "efficiently". (-50 gold, +4 :mad:)
 
Back
Top Bottom