Guide to event modding in BtS

Could you please explain what the text

%s1_civ_adjective

does in the text files for events? I know those abbreviations are for various civ names, city names and such, but how does the game determine what they mean? They seem unpredictable in use. Is there a tutorial anywhere for this sort of thing?
 
Do you have Python logging enabled in the *.ini file? If so, are you getting any messages in \My Documents\My Games\Beyond the Sword\Logs ? They should be recorded in PythonErr.log. That will tell you for sure if the Python script is throwing any errors. If the event trigger isn't set right, the rest of the event won't fire. You only had the EVENTINFOS file XML on the other thread -- you do have a setup in EVENTTRIGGERINFOS for the event too, correct?

It looks like you have the Python Callback line setup correctly in the XML.
 
Alright, I've got it working. Turns out that contrary to this thread's instructions, eReligion comes from argsList[1], not argsList[0].
 
Could you please explain what the text

%s1_civ_adjective

does in the text files for events? I know those abbreviations are for various civ names, city names and such, but how does the game determine what they mean? They seem unpredictable in use. Is there a tutorial anywhere for this sort of thing?

The answer is in the question...- its the adjective name for the city, using basic grammer rules.
It corresponds to, for example, "the English city of London has been hit by a hurricane".

%s1 is used in the third person- ie used in the broadcast summaries of events that other civs see.
%s2 is used when the event is "talking" to you specifically. Using the example above, it would be used to say, to you, "London has been hit by a hurricane".


HDK
 
Hi All,
First time event creater,& python virgin:)

Im trying to get a peasant army to appear within the radius of a city, due to a 'peasant rebellion' around that city. (ive posted the orginating event in Solvers Events Mod thread.)

1.The peasants will obviously count as barbarians, but i want them to appear (hopefully named as Peasants) even if barbarians are disabled- is this possible? Perhaps I need tocreate a 'peasant' pikeman, etc unit?
2. I would like to increase the no. of cites affect in a further event, linked from this one- eg, 3 cities affected, so 3 armies created.
3. I want to balance the units within the armies- ie. i want to include Pikemen, Archers & Catapults in a 3:1:1 ratio.

Im trying to adapt jkp1187's Mahdi/Taliban army python code; i think im close to getting things correct, but something still is wrong :(

This is the python code i have inserted at the end of the CvRandomEventInterface.py; it is a copy/paste/slight mod of the Taliban code.
Spoiler :
######## PEASANT_REBELLION ###########

def canTriggerPeasantRebellion(argsList):

kTriggeredData = argsList[0]
player = gc.getPlayer(kTriggeredData.ePlayer)

# Find an eligible plot
map = gc.getMap()
for i in range(map.numPlots()):
plot = map.plotByIndex(i)
if (plot.getOwner() == -1 and not plot.isWater() and not plot.isImpassable() and plot.area().getCitiesPerPlayer(kTriggeredData.ePlayer) > 0 and plot.isAdjacentPlayer(kTriggeredData.ePlayer, true)):
return true

return false


def getHelpPeasantRebellion2(argsList):
iEvent = argsList[0]
kTriggeredData = argsList[1]

szHelp = localText.getText("TXT_KEY_EVENT_PEASANT_REBELLION_HELP_2", ())

return szHelp


def applyPeasantRebellion2(argsList):
iEvent = argsList[0]
kTriggeredData = argsList[1]
player = gc.getPlayer(kTriggeredData.ePlayer)

listPlots = []
map = gc.getMap()
for i in range(map.numPlots()):
plot = map.plotByIndex(i)
if (plot.getOwner() == kTriggeredData.ePlayer and not plot.isWater() and not plot.isImpassable() and plot.area().getCitiesPerPlayer(kTriggeredData.ePlayer) > 0, true)):
listPlots.append(i)

if 0 == len(listPlots):
return

plot = map.plotByIndex(listPlots[gc.getGame().getSorenRandNum(len(listPlots), "Peasant Rebellion event location")])

if map.getWorldSize() == CvUtil.findInfoTypeNum(gc.getWorldInfo, gc.getNumWorldInfos(), 'WORLDSIZE_DUEL'):
iNumUnits1 = 2
iNumUnits2 = 1
elif map.getWorldSize() == CvUtil.findInfoTypeNum(gc.getWorldInfo, gc.getNumWorldInfos(), 'WORLDSIZE_TINY'):
iNumUnits1 = 3
iNumUnits2 = 1
elif map.getWorldSize() == CvUtil.findInfoTypeNum(gc.getWorldInfo, gc.getNumWorldInfos(), 'WORLDSIZE_SMALL'):
iNumUnits1 = 5
iNumUnits2 = 2
elif map.getWorldSize() == CvUtil.findInfoTypeNum(gc.getWorldInfo, gc.getNumWorldInfos(), 'WORLDSIZE_STANDARD'):
iNumUnits1 = 7
iNumUnits2 = 3
elif map.getWorldSize() == CvUtil.findInfoTypeNum(gc.getWorldInfo, gc.getNumWorldInfos(), 'WORLDSIZE_LARGE'):
iNumUnits1 = 8
iNumUnits2 = 4
else:
iNumUnits1 = 7
iNumUnits2 = 2

iUnitType1 = CvUtil.findInfoTypeNum(gc.getUnitInfo, gc.getNumUnitInfos(), 'UNIT_PIKEMEN')
iUnitType2 = CvUtil.findInfoTypeNum(gc.getUnitInfo, gc.getNumUnitInfos(), 'UNIT_ARCHER')

barbPlayer = gc.getPlayer(gc.getBARBARIAN_PLAYER())
for i in range(iNumUnits1):
barbPlayer.initUnit(iUnitType1, plot.getX(), plot.getY(), UnitAITypes.UNITAI_ATTACK_CITY_LEMMING, DirectionTypes.DIRECTION_SOUTH)
for i in range(iNumUnits2):
barbPlayer.initUnit(iUnitType2, plot.getX(), plot.getY(), UnitAITypes.UNITAI_ATTACK_CITY_LEMMING, DirectionTypes.DIRECTION_SOUTH)


I reckon my main mistakes are in the plot location area- ie. select the are for the army to appear in. The event fires fine from XML;so the mistake is definately in the python code. Th CivEventInfos python tags are here:
Spoiler :
<PythonCallback>applyPeasantRebellion2</PythonCallback>
<PythonExpireCheck/>
<PythonCanDo/>
<PythonHelp>getHelpPeasantRebellion2</PythonHelp>


Please, can anyone tell me what im doing wrong? And maybe help with the more complicated requests above :)

Many thanks,
HDK
 
Headlock,

The plot for all generic barbarian events (which I adapted for my Mahdi Army and Taliban events, and also the pirate events,) is controlled via Python, not the XML. See the line from the trigger script that says:

if (plot.getOwner() == -1 and not plot.isWater() and not plot.isImpassable() and plot.area().getCitiesPerPlayer(kTriggeredData.ePla yer) > 0 and plot.isAdjacentPlayer(kTriggeredData.ePlayer, true)):

First, I'm not sure if the gap in "ePla yer" in your post is from your script or just a bad copy and paste, but that obviously should be one word. If not, you will get an error.

Second, I highlighted the key part of the script that may be messing you up -- the barbarian events all select a plot that is outside the triggering player's culture borders, but also adjacent to those borders. You'll definitely want to remove this line both from the "trigger" script and from the "apply" script.

If you feel like downloading my NextWar mod, there is a special NextWar only event that generates barbarians inside the player's culture borders (but not necessarily within a city radius) called "THE_CYLONS". You may want to take a look at the script in that event.
 
Success! :king:

MANY thanks Jkp1187! :goodjob:

The cylons code worked perfectly (after a small bit of tweaking- i changed the req for Cloning to Liberalism, removed the need of a cloning factoy(or any building),changed the units spawned to Pikemen and Archers.) The positioning seems close/within the radius to the event city, too. :crazyeye:

Python code:
Spoiler :
######## PEASANT_REBELLION ###########
#Peasant Rebellion by Headlock. www.Civfanatics.com

def canTriggerPeasantRebellion(argsList):

kTriggeredData = argsList[0]
pPlayer = gc.getPlayer(kTriggeredData.ePlayer)
pTeam = gc.getTeam(pPlayer.getTeam())

# If Barbarians are disabled in this game, this event will not occur.
if gc.getGame().isOption(GameOptionTypes.GAMEOPTION_NO_BARBARIANS):
return false

# Let's be fair -- civ can't currently be at war.

if pTeam.getAtWarCount(true) > 0:
return false

# At least one civ on the board must know TECH_LIBERALISM
bFoundValid = false
iTech = CvUtil.findInfoTypeNum(gc.getTechInfo, gc.getNumTechInfos(), 'TECH_LIBERALISM')
for iPlayer in range(gc.getMAX_CIV_PLAYERS()):
loopPlayer = gc.getPlayer(iPlayer)
if loopPlayer.isAlive():
if gc.getTeam(loopPlayer.getTeam()).isHasTech(iTech):
bFoundValid = true
break
if not bFoundValid:
return false

# Find an eligible plot
map = gc.getMap()
for i in range(map.numPlots()):
plot = map.plotByIndex(i)
if (plot.getOwner() == kTriggeredData.ePlayer and not plot.isWater() and not plot.isImpassable() and not plot.isCity() and plot.getNumUnits() > 0 and plot.area().getCitiesPerPlayer(kTriggeredData.ePlayer) > 0):
return true

return false

# and plot.area().getCitiesPerPlayer(kTriggeredData.ePlayer) > 0

def getHelpPeasantRebellion2(argsList):
iEvent = argsList[0]
kTriggeredData = argsList[1]

szHelp = localText.getText("TXT_KEY_EVENT_PEASANT_REBELLION_HELP_2", ())

return szHelp

# and plot.area().getCitiesPerPlayer(kTriggeredData.ePlayer) > 0
def applyPeasantRebellion2(argsList):
iEvent = argsList[0]
kTriggeredData = argsList[1]
pPlayer = gc.getPlayer(kTriggeredData.ePlayer)

listPlots = []
map = gc.getMap()
for i in range(map.numPlots()):
plot = map.plotByIndex(i)
if (plot.getOwner() == kTriggeredData.ePlayer and not plot.isWater() and not plot.isImpassable() and not plot.isCity() and plot.getNumUnits() > 0 and plot.area().getCitiesPerPlayer(kTriggeredData.ePlayer) > 0):
listPlots.append(i)

if 0 == len(listPlots):
return

plot = map.plotByIndex(listPlots[gc.getGame().getSorenRandNum(len(listPlots), "Peasant Rebellion event location")])

if map.getWorldSize() == CvUtil.findInfoTypeNum(gc.getWorldInfo, gc.getNumWorldInfos(), 'WORLDSIZE_DUEL'):
iNumUnits1 = 3
iNumUnits2 = 1
elif map.getWorldSize() == CvUtil.findInfoTypeNum(gc.getWorldInfo, gc.getNumWorldInfos(), 'WORLDSIZE_TINY'):
iNumUnits1 = 4
iNumUnits2 = 1
elif map.getWorldSize() == CvUtil.findInfoTypeNum(gc.getWorldInfo, gc.getNumWorldInfos(), 'WORLDSIZE_SMALL'):
iNumUnits1 = 5
iNumUnits2 = 2
elif map.getWorldSize() == CvUtil.findInfoTypeNum(gc.getWorldInfo, gc.getNumWorldInfos(), 'WORLDSIZE_STANDARD'):
iNumUnits1 = 6
iNumUnits2 = 2
elif map.getWorldSize() == CvUtil.findInfoTypeNum(gc.getWorldInfo, gc.getNumWorldInfos(), 'WORLDSIZE_LARGE'):
iNumUnits1 = 7
iNumUnits2 = 3
else:
iNumUnits1 = 5
iNumUnits2 = 2

iUnitType1 = CvUtil.findInfoTypeNum(gc.getUnitInfo, gc.getNumUnitInfos(), 'UNIT_PIKEMAN')
iUnitType2 = CvUtil.findInfoTypeNum(gc.getUnitInfo, gc.getNumUnitInfos(), 'UNIT_ARCHER')

barbPlayer = gc.getPlayer(gc.getBARBARIAN_PLAYER())
for i in range(iNumUnits1):
barbPlayer.initUnit(iUnitType1, plot.getX(), plot.getY(), UnitAITypes.UNITAI_ATTACK_CITY_LEMMING, DirectionTypes.DIRECTION_SOUTH)
for i in range(iNumUnits2):
barbPlayer.initUnit(iUnitType2, plot.getX(), plot.getY(), UnitAITypes.UNITAI_ATTACK_CITY_LEMMING, DirectionTypes.DIRECTION_SOUTH)


This is excellent; now i can add proper slave armies to the slave revolt! It always annoyed me, that; having a revolt, but no rebellious gladiators to smite!

oh and nw Wotan:)

Now i can start to build up a nice little stack of events based around:
Peasant Riots-->Peasant Rebellion!
Factory Strike-->Industrial Unrest-->Communist Uprising!
Parlimentary Deadlock-->Political Chaos-->Collapse of Law and Order!
Military Mutiny-->Fascist Thugs-->Coup d'Etat!


The crucial missing piece now is to increase the number of cities affected from 1 to multiple cities (eg 3-5 cites collapse into chaos). Any suggestions, jkp?:)

Many thanks again :goodjob:
HDK
 
hello... I'm trying to write an event for the first time here --- I was wondering:

is it possible for one event to spawn another event, where the original event gets to choose where the new event happens (which city) and for whom it happens (which player)?

maybe there's a data structure out there where all events are listed, where i can append these new events (along with all their trigger data)? or perhaps there's another way to do this? (I assume you must do this in python - which shouldn't be a deal breaker for me.)


thanks in advance for any help and suggestions!
 
The crucial missing piece now is to increase the number of cities affected from 1 to multiple cities (eg 3-5 cites collapse into chaos). Any suggestions, jkp?:)

Many thanks again :goodjob:
HDK


I think you'd have to use Python to script an event that "loops" on several cities with certain criteria.
 
I am an event noob, so bear with with.

Event is.. having a whale attack a caravel and sink it.

based on real life story of the whaler Essex, which Herman melville used as a basis for Moby Dick.

Obviously..
* required tech is optics
* obsolete with steam power

unit disband command will destroy the caravel.

Couple of background questions..

a. Event map trigger is caravel in ocean square?
b. am thinking of including a trigger where player has to have whales as a resource..?
c. How do get the whale diving graphic to display after the caravel is sunk..?
d. better yet, how to import a speicalized whale graphic that is indeed white.. aka Moby Dick?
 
I'll be frank: I don't know how you'd go about adding the whale smashing your caravel graphics, though if you figure out how to do it, please post it!

Using the event system as described here, however, it would be very easy to trigger a random event where a caravel is sunk by a whale (which can be described in the text). You could set as a trigger the Caravel being on a square that has the Whale bonus resource on it. Or, perhaps the triggering civ just needs to have the whale resource.
 
First - awesome guide.

Second - I'm not sure how this event could work...

Suppose I wanted to make an event where a unit loses experience. For example, a ship's captain is deposed/dismissed and a newer, greener one is brought in his place.

Could I just set the unit experience gain to a negative number, or is that an illegal programming option?
 
How can I make an event add unhealthyness to a specific city? iHealth works as advertised for a positive number, but putting a negative number has it apply to the entire empire no matter what. How can I fix this?

Most probably your event isn't set to <bPickCity>1, as it should be here. Or maybe that option isn't set in the trigger - both need to be set to accomplish what you want.
 
Couple of questions.

1. reading the bPickPlayer definition: "...the trigger will pick another player to affect by the event." Meaning "...the trigger will pick another player to be affected by the event." Correct?

2. Here's the problem I ran into. I'm Player A, and Player B is the AI or another player. I want an event that triggers for Player A as a result of a tech learned by Player B. I filled out the OtherPlayerHasTech tag line (but not bPickPlayer since I don't want Player B to be affected in any way). The event doesn't trigger.

I'm assuming OtherPlayerHasTech will only work if bPickPlayer is switched on, correct?
 
That's right, yeah. bPickPlayer is another player - if you're human, it picks an AI. But OtherPlayerHasTech merely creates the possibility the event might happen. It won't trigger automatically when the other player gets that tech.
 
Back
Top Bottom