You must be logged in to post messages.
Please login or register

Scenario Design and Discussion
Moderated by Sebastien, Mr Wednesday

Hop to:    
loginhomeregisterhelprules
Bottom
Topic Subject: Quickguide to get started with the python library AoE2ScenarioParser
posted 08-25-21 10:49 AM CT (US)   
This is a short guide for how to get started with the python library AoE2ScenarioParser!

Python is a programming language, so some basic programming skills will come in handy. If you're interested in learning python, this is a good tutorial which covers most of the basics to get started.

The AoE2ScenarioParser is a library created by KSneijders for python which is used for editing parts of an aoe2scenario file from Age of Empires 2 Definitive Edition outside of the in-game editor. This tool can help you write scripts to create, copy and modify triggers for your scenario.

Step one: Installing python.

Go to the official python website and download the latest version of python.



Once downloaded. Run the setup file that you downloaded and follow the instructions to install python.

Step two
: Installing an IDE (Integrated Development Environment)

I use PyCharm to write python programs, so this will just show how to setup PyCharm. There are A LOT of different IDEs for many different programming languages out there. If you prefer another IDE, you can skip this step.

Go to PyCharm's download page and download the community edition.



Once the file is downloaded, run it and follow the instructions to install it.

Step three: Installing the library

Once you have installed PyCharm. You have to install the AoE2ScenarioParser library. I installed it after I created a new project, so this is how I will show you how to install it.

Create a new project in PyCharm. Click "File" in the upper left corner of PyCharm and select "New Project...":



Make sure it is saved somewhere you can easily find it and call it something relevant to what you're going to do with it. I called mine ScenarioParserProject for example:



Click Create. A message might appear asking if you want to open this new project in a new window or in the same window. It doesn't matter which one you choose.

Now you can install the AoE2ScenarioParser library. You do this by clicking the little "Terminal" button at the bottom of the project window:



And writing "pip install AoE2ScenarioParser" and pressing enter.

There you have it, you can now use the AoE2ScenarioParser library in your python PyCharm project! You do that by importing the AoE2ScenarioParser files using the line:

from AoE2ScenarioParser.scenarios.aoe2_de_scenario import AoE2DEScenario

Which you write at the top of your python script.

Like the triggers in AoE2, your python program will execute from top to bottom. So the first thing you do is declaring the variables that will be used throughout the project, for example your file input path and output path. The input file must already exist, you can't create scenarios from scratch, only edit existing scenarios and writing to a new file.

input_path = "C:\\Users\\Basse\\Games\\Age of Empires 2 DE\\76561198050198569\\resources\\_common\\scenario\\parserTestInput.aoe2scenario"
output_path = "C:\\Users\\Basse\\Games\\Age of Empires 2 DE\\76561198050198569\\resources\\_common\\scenario\\parserTestOutput.aoe2scenario"

The double backslashes are required for python to interpret the single backslashes in your file path.

After that you have to open your scenario. This is done with this line of code:

scenario = AoE2DEScenario.from_file(input_path)

At the end of your program, you use this line of code to generate the output file:

scenario.write_to_file(output_path)

Between those two lines of code you can write your code to edit your scenarios. To edit the triggers of a scenario, create a trigger_manager object which we can modify:

trigger_manager = scenario.trigger_manager

Then you can in code write something like this, to create a trigger that spawns militias for each player in a row:

from AoE2ScenarioParser.scenarios.aoe2_de_scenario import AoE2DEScenario

input_path = "C:\\Users\\Basse\\Games\\Age of Empires 2 DE\\76561198050198569\\resources\\_common\\scenario\\parserTestInput.aoe2scenario"

output_path = "C:\\Users\\Basse\\Games\\Age of Empires 2 DE\\76561198050198569\\resources\\_common\\scenario\\parserTestOutput.aoe2scenario"

scenario = AoE2DEScenario.from_file(input_path)

trigger_manager = scenario.trigger_manager

spawn_array_x = [1,1,1,1,1,1,1,1]
spawn_array_y = [1,2,3,4,5,6,7,8]
for player in range(1,9):
trigger = trigger_manager.add_trigger(f"Spawn Militia p({player})")
trigger.looping=True
trigger.new_condition.timer(
timer=20
)
trigger.new_effect.create_object(
object_list_unit_id=74,
source_player=player,
location_x=spawn_array_x[player - 1],
location_y=spawn_array_y[player - 1]
)
trigger.new_effect.task_object(
object_list_unit_id=74,
source_player=player,
location_x=10,
location_y=spawn_array_y[player - 1],
area_x1=spawn_array_x[player - 1],
area_y1=spawn_array_y[player - 1],
area_x2=spawn_array_x[player - 1],
area_y2=spawn_array_y[player - 1]
)
scenario.write_to_file(output_path)

When you have your code ready and error free, press the "Run" button in the upper right corner:



This will output a scenario with these triggers:



Alian713 created this example where the scenario ha 20% chance of creating a palisade wall on every tile of the map, every time the map is played. This results in a HUGE amount of triggers, but the result is cool to see how much can be done with just a few lines of code:

from AoE2ScenarioParser.scenarios.aoe2_de_scenario import AoE2DEScenario
from AoE2ScenarioParser.datasets.players import PlayerId

input_path = "C:\\Users\\Basse\\Games\\Age of Empires 2 DE\\76561198050198569\\resources\\_common\\scenario\\parserTestInput.aoe2scenario"

output_path = "C:\\Users\\Basse\\Games\\Age of Empires 2 DE\\76561198050198569\\resources\\_common\\scenario\\parserTestOutput.aoe2scenario"

scenario = AoE2DEScenario.from_file(input_path)

trigger_manager = scenario.trigger_manager

for y in range(scenario.map_manager.map_height):
for x in range(scenario.map_manager.map_width):
trigger1 = trigger_manager.add_trigger(f"Spawn Palisade at ({x}, {y}) 1/2")
trigger1.new_condition.chance(
quantity=20
)
trigger1.new_effect.create_object(
object_list_unit_id=72,
source_player=PlayerId.ONE,
location_x=x,
location_y=y
)
trigger2 = trigger_manager.add_trigger(f"Spawn Palisade at ({x}, {y}) 2/2")
trigger2.new_effect.deactivate_trigger(
trigger_id=trigger1.trigger_id
)

scenario.write_to_file(output_path)

Which creates 28800 triggers for a 120x120 map!







I hope this will help people get started with the AoE2ScenarioParser library for python! Please feel free to ask questions. I will try my best to answer

I posted some more example codes snippets here.

KSneijders have also created some documentation here: https://ksneijders.github.io/AoE2ScenarioParser/
And the github page offers some additional help if you know how to navigate the repository: https://github.com/KSneijders/AoE2ScenarioParser

There is also a discord group: https://discord.gg/DRUtmugXT3

[This message has been edited by Basse (edited 08-29-2021 @ 04:30 AM).]

Replies:
posted 08-25-21 06:43 PM CT (US)     1 / 3  
Damn, that's pretty cool!
Coolest use scenario I could think of so far of would be for some kind of noise-based layout generation. Like a dungeon crawler minigame whereas the map regens itself every time the player gets through the current floor.
Would need to be pretty mini, most likely (else performance would go to sh*t )

The Fall of Hummaria -- Teaser [4.2] -- Project's Thread
Cavern Pirates -- The Treasure Hunt [4.6] -- Captain's Revenge
My Blacksmith

[This message has been edited by rewaider (edited 08-25-2021 @ 06:44 PM).]

posted 08-26-21 01:35 AM CT (US)     2 / 3  
The generation of the random object spawn only takes about 1 second, then no other triggers fire so it shouldn't affect performance!

I'll try to add more examples later, will probably just copy and paste some from the discord group
posted 08-26-21 09:37 AM CT (US)     3 / 3  
Here's another example by Alian713 for how to copy triggers from one scenario to another (you have to load both scenarios before this code snippet):

# define references
src_trigger_manager = src_scenario.trigger_manager
trg_trigger_manager = trg_scenario.trigger_manager
trg_triggers = trg_trigger_manager.triggers

# for this example, I'm gonna just add everything from my source map, but you can make a more filtered list
# make sure this list has the
trigger_list = src_trigger_manager.triggers

# insert after the 10th trigger of the src map
insertion_point = 10


trigger_id_map = {}
for i, trigger in enumerate(trigger_list):
# map the old IDs to their new IDs
trigger_id_map[trigger.trigger_id] = insertion_point+i

# set the new IDs
trigger.trigger_id += insertion_point+i

# correctly offset the triggers being inserted
for trigger in trigger_list:
for i, effect in enumerate(trigger.effects):
if effect.effect_type in [EffectId.ACTIVATE_TRIGGER, EffectId.DEACTIVATE_TRIGGER]:
# update the trigger_id of an effect that (de)activates another trigger
# if an effect is (de)activating a trigger that is not being copied over, invalidate that effect.
effect.trigger_id = trigger_id_map.get(effect.trigger_id, -1)

# update the trigger ID of all the triggers after the insertion point
for trigger in trg_triggers[insertion_point:]:
trigger.trigger_id += len(trigger_list)
for effect in trigger.effects:
if effect.effect_type in [EffectId.ACTIVATE_TRIGGER, EffectId.DEACTIVATE_TRIGGER] and\
effect.trigger_id >= insertion_point:
effect.trigger_id += insertion_point

trg_triggers = trg_triggers[:insertion_point]+trigger_list+trg_triggers[insertion_point:]


Here are two examples how to circumvent the text limit of the script call effect, so you can import entire .xs files into a single script call and even define xs rule in game (which currently do not parse when added manually in game!):

Import a long xs file in a single script call:

trigger = trigger_manager.add_trigger("Import xs Script")
trigger.new_effect.script_call(
message="""
`your xs file content goes here´
"""
)

Add a rule to script call (this one just converts kills to food for every player):

kills = trigger_manager.add_trigger("Check kills")
kills.new_effect.script_call(
message="""
rule checkKills
active
highFrequency
{
for(i=1; <9)
{
if(xsPlayerAttribute(i, cAttributeKills) >= 1)
{
xsSetPlayerAttribute(i, cAttributeFood, xsPlayerAttribute(i, cAttributeFood)+50);
xsSetPlayerAttribute(i, cAttributeKills, xsPlayerAttribute(i, cAttributeKills)-1);
}
}
}
"""
)

[This message has been edited by Basse (edited 08-26-2021 @ 09:45 AM).]

Age of Kings Heaven » Forums » Scenario Design and Discussion » Quickguide to get started with the python library AoE2ScenarioParser
Top
You must be logged in to post messages.
Please login or register
Hop to:    
Age of Kings Heaven | HeavenGames