WML for Complete Beginners: Chapter 5
Contents
Interlude: Testing your WML
Having completed chapter 4, the game should be able to load your campaign. The next chapter will make your campaign more interesting, but already it's worth regularly loading the campaign in the game - at some stage you'll make some typos, and the earlier you notice that it's broken the less code you'll have to read to find the bug.
To make the game notice changes to the WML files, either press F5 on the title screen, or start the game from the command line with the --nocache option.
There are various Maintenance tools which can help with checking for errors. (Version 1.15.? and later only) There will be a lot of work on a new tool, described in the ValidationFAQ; however as of 1.15.5 it's not user-friendly yet.
Chapter 5: Events
Let's walk through an average morning. When your alarm clock goes off, you wake up. You go downstairs and put some bread in the toaster for breakfast. When the toaster pops, you butter the toast and eat it. When you have eaten breakfast, you go outside and wait for the schoolbus to arrive. When the bus arrives, you get on it. When it stops at your destination, you get off of the bus.
Notice that you do everything “when” something else happens. These are all “events”. The alarm going off caused you to wake up. The toaster popping causes you to butter the toast and eat it. Finishing breakfast go outside. The bus stopping causes you to get on or off. These are all like events in WML.
To give you examples of the real thing, i.e. playing Wesnoth: When you move somewhere, it’s an event. When you attack somebody, it’s an event. When you are hit, it’s an event. When a new turn starts, it’s an event. When you kill, or get killed, these are too - obviously – events.
It’s up to your decisions, which of these events you want to “catch”, to make them a turning point in the storyline.
The syntax for writing an event goes like this:
[event] name=name_of_the_event #do something [event]
As mentioned before, there are quite a few events available to you in WML. Of these, the most commonly used are the prestart event, the start event, and the moveto event. In our example here we shall also mention the last breath event, the die event, and the time over event.
Common Events
Now, I'm not going to supply you with an exhaustive list of every kind of predefined event and what each does, that's what the EventWML page is for. I will, however, provide you with a list of the most frequently used predefined events and what they do, since we will be using these events in our campaign scenarios later on.
The "prestart" Event
- This event fires before anything is shown on the screen for your scenario. This event is commonly used to recall loyal heroes for the player and to create loyal defenders. Anything you want to happen before the user can even see the map goes within this event. Think of it this way: everthing in the prestart event is done during the loading-screen before the scenario begins. To create this event, we use the following WML:
[event] name=prestart #do stuff here [/event]
- The prestart event does not require any additional keys (besides name=prestart) in order to function. However, we will not use this event in our sample scenario.
The "start" Event
- The "start" event fires after the user can see the map and the screen, but before the user can actually do anything. To declare a start event, simply use
name=start
- Objectives
A common use of the start event is to declare the objectives (The little window you can access by pressing CTRL + J) for the scenario.
One very important distinction here: The [objectives] tag only serves to display the objectives at the start of the scenario, NOT to program them into the game logic. The actual logic of defining the defeat/win outcome is written into other events, using the [endlevel] tag, as we shall see later. Now, back to the initial display of the objectives.
Declaring the objectives is done using two tags: [objectives] and [objective] like this:
[event] name=start [objectives] [objective] description= _ "Defeat the enemy leader" condition="win" [/objective] [objective] description= _ "Death of your leader" condition="lose" [/objective] [/objectives] [/event]
- Notice how the [objectives] tag encloses all the [objective] tags. Basically the [objectives] tag tells WML that you are going to start declaring objectives for the scenario, which are then represented by [objective] tags. Notice that the [objective] tag has two keys
description= _ "Describe the Objective Here" condition= #put either "win" or "lose" here--win objectives are grouped together in green and lose objectives are grouped together in red.
- [message]
Of course, the start event is also a good place to create an initial dialogue. This is done using the [message] tag. This tag has two keys:
speaker= message= _ ""
The speaker tag contains the id of the unit who is going to talk. Remember in the last chapter how I gave our example leader an id of 'MyLeader'? Well, now he's going to give a short speech:
speaker=MyLeader
The message key represents what the speaker is actually going to say. This should be marked translatable:
message= _ "I see the orcs!"
I'm going to put this (and another) message into our example start event:
[event] name=start [message] speaker=MyLeader message= _ "I see the orcs!" [/message] [message] speaker=EnemyLeader message= _ "Grrrr!" [/message] [objectives] [objective] description= _ "Defeat the enemy leader" condition="win" [/objective] [objective] description= _ "Death of your leader" condition="lose" [/objective] [/objectives] [/event]
While it doesn't matter to WML where you put events, it is good form to put them below the [side] tags so that others can read your WML more easily:
- textdomain wesnoth-my_first_campaign
[scenario] id=my_first_scenario next_scenario=null name=_"My First Scenario." map_data="{~add-ons/my_first_campaign/maps/my_first_map.map}" turns=30 [side] side=1 controller=human team_name="good" user_team_name= _ "My Team" id=MyLeader name= _ "My Leader's Name" type="Elvish Ranger" unrenameable=yes canrecruit=yes recruit="Elvish Fighter, Elvish Archer, Elvish Shaman" gold=100 [/side] [side] side=2 controller=ai team_name="bad" user_team_name= _ "Bad Guys" id="EnemyLeader" name= _ "My Villain" type= "Orcish Warrior" unrenameable=yes canrecruit=yes recruit="Orcish Grunt, Orcish Archer, Orcish Assassin, Wolf Rider" gold=100 [/side] [event] name=start [message] speaker=MyLeader message= _ "I see the orcs!" [/message] [message] speaker=EnemyLeader message= _ "Grrrr!" [/message] [objectives] [objective] description= _ "Defeat the enemy leader" condition="win" [/objective] [objective] description= _ "Death of your leader" condition="lose" [/objective] [/objectives] [/event] [/scenario]
The "moveto" Event
As you might have guesses, the "moveto" event covers when a unit moves. This event triggers after any unit moves and matches a given [filter]. What is this '[filter]' you ask? Let's take a quick look:
- [filter]
The [filter] tag tells WML to apply this event only to units which match the filter. For example, what if I wanted to run my moveto event only when side 1 moves a unit onto hex 1,1? In that case, I would use:
[filter] side=1 x,y=1,1 [/filter]
Simple, huh? Other common keys for a filter tag include:
id=MyLeader #id of a specific unit, such as our leader. type="Elvish Shaman" #only Elvish Shamans would pass this filter.
To learn more about [filter]s, check out FilterWML. Now we could add a very simple moveto event to our example scenario:
[event] name=moveto first_time_only="no" [filter] side=1 x,y=1,1 [/filter] [message] speaker=unit #unit means the unit triggering this event--in this case the guy who just moved message= _ "Look at me! I'm on hex 1,1!" [/message] [/event]
"But you added a new key without explaining it...I'm confused!" Hang in there, I'm just getting to explaining that first_time_only key.
- first_time_only
The "first_time_only" key has two possible values "yes" and "no". This key is actually present in every event, even if you don't type it in. In that case, it contains a value of "yes" (this is also called a default value). If first_time_only="yes", the event will only run the first time its conditions are met. In our example, this means that only the first unit to move onto hex 1,1 will announce his presence to the world. If, however, first_time_only="no" then every time a unit moves onto hex 1,1 will cause it to speak. This is what the example event does.
The "time over" Event
This event fires after all turns have run out. It is usually used to give a brief message before causing the player to lose the scenario. By the way, forcing a win/loss using WML is accomplished like this:
[endlevel] result=victory #or result=defeat to force a loss. [/endlevel]
A rather typical time over event would look like this:
[event] name=time over [message] speaker=MyLeader message= _ "I give up. This is taking too long..." [/message] [endlevel] result=defeat [/endlevel] [/event]
"last breath" and "die" Events
These two events are very similar. Both events trigger when a unit (specified by [filter], known as 'unit') is killed by another unit (can be specified by [filter_second], known as 'second_unit'). However there is one, crucial difference. "last breath" occurs before a unit's death-animation is shown (before the unit visibly dies, but has <= 0 hitpoints) whereas "die" occurs immediately after the unit's death-animation. As a result, use "last breath" when you want the dying unit to give a last-breath message, and "die" for anything which occurs as a result of that death. Here is an example of using both of these events. See if you can figure out what exactly is happening:
[event] name="last breath" first_time_only=no [filter] side=2 [/filter] [filter_second] side=1 [/filter_second] [message] speaker=second_unit message= _ "Take that!" [/message] [message] speaker=unit message= _ "Hah! You missed!" [/message] [/event] [event] name=die first_time_only=no [filter] side=2 [/filter] [filter_second] side=1 [/filter_second] [message] speaker=second_unit message= _ "Wrong!" [/message] [/event]
These two events fire every time side 1 kills one of side 2's units. When these events fire, here is what the player will see: "Take that!" -> "Hah! You missed!" -> Death animation -> "Wrong!"
Nested Events
Have you ever wondered what would happen if you did this (pseudocode example):
[event] name=event1 (...) [event] name=event2 (...) [/event] [/event]
This is called nesting events because one event is "nested" inside the other. What happens here is that when event1 is triggered, in addition to whatever else event1 does, event2 is created. This prevents event2 from occurring before event1. Suppose we wanted to display a message after defeating the enemy leader (id=EnemyLeader) and moving our leader (id=MyLeader) to the enemy's keep (x,y=20,7). We could do that like this:
[event] name=die [filter] id=EnemyLeader [/filter] [event] name=moveto [filter] id=MyLeader x,y=20,7 [/filter] [message] speaker=unit message= _ "Haha! I captured the enemy keep!" [/message] [/event] [/event]
- Further Information
For more information on events, how to write them, and how they work, go to the page on EventWML.
I'm going to insert a few of these sample events into our example scenario (and write a new "die" event for the enemy leader), which now looks like:
#textdomain wesnoth-my_first_campaign [scenario] id=my_first_scenario next_scenario=null name=_"My First Scenario." map_data="{~add-ons/my_first_campaign/maps/my_first_map.map}" turns=30 [side] side=1 controller=human team_name="good" user_team_name= _ "My Team" id=MyLeader name= _ "My Leader's Name" type="Elvish Ranger" unrenameable=yes canrecruit=yes recruit="Elvish Fighter, Elvish Archer, Elvish Shaman" gold=100 [/side] [side] side=2 controller=ai team_name="bad" user_team_name= _ "Bad Guys" id="EnemyLeader" name= _ "My Villain" type= "Orcish Warrior" unrenameable=yes canrecruit=yes recruit="Orcish Grunt, Orcish Archer, Orcish Assassin, Wolf Rider" gold=100 [/side] [event] name=start [message] speaker=MyLeader message= _ "I see the orcs!" [/message] [message] speaker=EnemyLeader message= _ "Grrrr!" [/message] [objectives] [objective] description= _ "Defeat the enemy leader" condition="win" [/objective] [objective] description= _ "Death of your leader" condition="lose" [/objective] [objective] description= _ "Turns run out" condition="lose" [/objective] [/objectives] [/event] [event] name=moveto first_time_only="no" [filter] side=1 x,y=1,1 [/filter] [message] speaker=unit #unit means the unit triggering this event--in this case the guy who just moved message= _ "Look at me! I'm on hex 1,1!" [/message] [/event] [event] name="last breath" first_time_only=no [filter] side=2 [/filter] [filter_second] side=1 [/filter_second] [message] speaker=second_unit message= _ "Take that!" [/message] [message] speaker=unit message= _ "Hah! You missed!" [/message] [/event] [event] name=die first_time_only=no [filter] side=2 [/filter] [filter_second] side=1 [/filter_second] [message] speaker=second_unit message= _ "Wrong!" [/message] [/event] [event] name=die [filter] id=EnemyLeader [/filter] [message] speaker=second_unit message= _ "Yeah! I killed him!" [/message] [endlevel] result=victory [/endlevel] [/event] [event] name=time over [message] speaker=MyLeader message= _ "I give up. This is taking too long..." [/message] [endlevel] result=defeat [/endlevel] [/event] [/scenario]
Congratulations! You have now written your first functional scenario! Go, give it a try! Play it! When you're ready to keep going, head to the next chapter.
Navigation | ||
---|---|---|
Chapter 4 Creating Your First Scenario |
WML for Complete Beginners: Chapter 5 | Chapter 6 Custom Units |