Latest Posts

Changes in WikiSandbox

Revision Differences of Revision 80

Test [[ wikisyntax | wiki syntax ]] here :-) ¶

[TOC] ¶

Some initial help for creating a campaign. What you should know before reading on: ¶

* [First steps for a scenario](https://wl.widelands.org/docs/wl/tutorial/) ¶
* Make sure to use a plain text editor when writing scripts ¶
* You may also take a look in the scripting files of the implemented campaigns, which you can find in the data directory of you widelands installation. Look into the folder data/campaigns. ¶

To follow this example, create an empty map with only one player set at position 0,0 . To find this position watch the values at the bottom right in the editor. This values represents: (x position, y position, height)
. Make sure you have set the tribe 'Empire'.

## init.lua ¶

You have read that the file `scripting/init.lua` is the main entry for your scenario. So this file is the starting point and should contain necessary global settings. This file contains usually different parts: ¶

### Including global fnctionality ¶

~~~~ ¶
include "scripting/coroutine.lua" ¶
include "scripting/messages.lua" ¶
include "scripting/ui.lua" ¶
include "scripting/richtext.lua" ¶
~~~~ ¶

The files which get included here contain functions which you may need in your project. E.g. the file `coroutine.lua` contain a function `sleep(time)`. Those files (and their functions) are described in [Auxiliary Scripts](https://wl.widelands.org/docs/wl/autogen_toc_auxiliary/). Please take a look at the includes above and look into the auxiliary scripts documentation to get knowledge about their functions. Don't worry if you don't understand that much at the moment. Later on we will see how to use them. ¶

### Global variables ¶

In this part of the `init.lua` we define variables which we want to be available each time. That's why they are called 'global' variables. This global variables are shortcuts and prevent writing each time the same: ¶

~~~~ ¶
plr = wl.Game().players[1] ¶
map = wl.Game().map ¶
plr_sf = map.player_slots[1].starting_field ¶
~~~~ ¶

What this code does: ¶

* `plr = wl.Game().players[1]`: This reads the first defined player of the map and assign it to the variable 'plr'. You have to set the player(s) in the map editor to get this to work. ¶
* `map = wl.Game().map`: The variable 'map' gives you now access to many properties of the map itself, e.g. to the function `get_field()` in [Map](https://wl.widelands.org/docs/wl/autogen_wl_map/#wl.map.Map). This will be the most used function in your scenario. ¶
* `plr_sf = map.player_slots[1].starting_field`: You have set a player in the map editor on a field, so this assigns this field to the global variable 'plr_sf'. Note that we use here already the variable 'map' which we have defined one line above. Also note that there is a point between 'map', 'player_slots' and 'starting_field'. I tell you later why this is important. ¶

### Including the main control script ¶

Now we are ready with initialization and can work on to get the story alive. To split of initialization and other stuff, we include another file in the last line in our `init.lua` : ¶

~~~~ ¶
include "map:scripting/
smain_tohry_scriptead.lua" ¶
~~~~ ¶

The file `
smain_tohry_scriptead.lua` will contain the main thread for our scenario. The include at the end of the `init.lua` makes sure all other initialization is done and all defined variables are available in each further included file. ¶

##
smain_tohry_scriptead.lua ¶

In this file we create our scenario and add conditions and objectives a player has to fulfill. ¶

The map you have created is empty by default. You have set a player, but no buildings at all. It is our part to place buildings. ¶

### Place buildings ¶

First we place the headquarters for player 1. Do you remember you have set the global variables `plr` and `plr_sf`? Here we use them: ¶

~~~~ ¶
local hq = plr:place_building("empire_headquarters", plr_sf, false, true) ¶
~~~~ ¶

We call the function [place_building()](https://wl.widelands.org/docs/wl/autogen_wl_bases/#wl.bases.PlayerBase) with the needed arguments (the entries in the brackets). The first argument for the function is a string. You could find the right string in the corresponding file init.lua of the building in the subfolders of _'data/tribes/buildings/'_. You need the string of the _'name'_ attribute here. This function returns a reference to the created buildings. In this example this is stored in the __local__ variable hq. ¶

__Importand remark about local:__ Whenever you assign variables with values, you should do it this way: `local name_variable = value`. The keyword local makes sure that this variable is only valid in this scope (file or function). The only exception: If you really know that you will use this variable elsewhere, you can omit the keyword `local`. But in this case you should give the variable a name which is absolutely clear! ¶

Note the colon between `plr` and the functions name. Do you remember that we had already a point as a separator? Time for a small explanation about the difference between point and colon: ¶

Consider to have an object like a car. This car has a property: color. But this car has also some functions: drive_straight(), drive_left(), drive_right(). If you want to get the color, you want to access the property: `clr = car.color.` If you want to drive the car you need to call the functions: `car:drive_straight(100 meter)`. Do you see the difference? Accessing a property needs a point, calling a function needs a colon! If you go through the documentation you may notice that some entries have brackets, and others not. The entries with brackets are functions, other are properties. Examples: ¶

* Properties (can be accessed through a point): [Player.name](https://wl.widelands.org/docs/wl/autogen_wl_game/#wl.game.Player.name) ¶
* Functions (must be called with a colon): [Player:sees_field(f)](https://wl.widelands.org/docs/wl/autogen_wl_game/#wl.game.Player.sees_field) ¶

Now its time for a first test. Run: ¶

~~~~ ¶
./widelands --scenario=/full/path/to/the/map.wmf ¶
~~~~ ¶

You should see the placed headquarters after loading is complete. Look at the stock... the headquarters is empty, no wares and workers are in it. We should change that. ¶

### Fill buildings ¶

The function [place_building()](https://wl.widelands.org/docs/wl/autogen_wl_bases/#wl.bases.PlayerBase) returns a reference to the created building. We have assigned this reference to the local variable `hq`. ¶

Now we can use this variable and the function [set_wares()](https://wl.widelands.org/docs/wl/autogen_wl_map/#wl.map.HasWares.set_wares) to fill the building: ¶

~~~~ ¶
hq:set_wares("log",10) ¶
hq:set_wares("planks",10) ¶
hq:set_wares("granite",10) ¶
~~~~ ¶

Because setting wares is a common task, there is a shortcut by passing a table to set_wares(): ¶

~~~~ ¶
hq:set_wares({ ¶
log = 10, ¶
planks = 20, ¶
granite = 20, ¶
marble = 5, ¶
}) ¶
~~~~ ¶

Same goes for setting workers
and soldiers: ¶

~~~~ ¶
hq:set_workers({ ¶
empire_builder = 5, ¶
empire_carpenter = 2, ¶
empire_lumberjack = 5, ¶
})


hq:set_soldiers({0,0,0,0}, 45)

~~~~ ¶

Like with the buildings name you will find the right names for wares and workers in the corresponding _'init.lua'_ in the subfolders of _'data/tribes/'_. Add as many wares and workers as you like. After your'e done you should make a test run again. ¶

The build in campaigns have those initial settings in an extra file called _'starting_conditions.lua'_ and included it through _'init.lua'_
. This is good habit so create a new file _'starting_conditions.lua'_ ,move the inital settings to that file and include it in _'init.lua'_ on line before the include of the main_thread: ¶
~~~~ ¶
... ¶
include "map:scripting/starting_conditions.lua" ¶
include "map:scripting/
smain_tohry_scriptead.lua" ¶
~~~~ ¶

### Create a message box ¶

You may have seen that our scenarios have some message boxes to infrom the player about the story. In the `init.lua` we have included `messages.lua`. This file contains convenient functions for messages. We are using [campaign_message_box()](https://wl.widelands.org/docs/wl/autogen_auxiliary_messages/#campaign_message_box) and add it to the `story_script.lua`: ¶

~~~~ ¶
campaign_message_box({title = "This is the title", body = p("Hi, this is my first scenario")}) ¶
~~~~ ¶

We give the function a table (enclosed with curly braces {}). The title is the window title, the body is the text. The text must be formatted with the richtext system, so we wrap it in a p-tag. See [richtext.lua](https://wl.widelands.org/docs/wl/autogen_auxiliary_richtext/) for further formatting functions. You may have more than one message box in your campaign. For better readability of your main script in _
smain_tohry_scriptead.lua_ it is ususally better to put the table(s) in an extra file. The built in campaigns have them usually in a file called _'texts.lua'_ which get included in the [include block of your init.lua](#including-global-fnctionality) or at the beginning of _'smain_tohry_scriptead.lua'_. This will look then like: ¶

File _'texts.lua_': ¶
~~~~ ¶
first_message = { ¶
title = "This is the title", ¶
body = p("Hi, this is my first scenario") ¶
} ¶
~~~~ ¶

File _'
smain_tohry_scriptead.lua'_: ¶
~~~~ ¶
-- Including tables of text ¶
include "map:scripting/texts.lua" ¶
... ¶
campaign_message_box(first_message) ¶
~~~~ ¶

The first line in the above example (beginning with two hyphens) is a comment. You should comment your code as mush as possible but not more than needed. ¶

### Triggering actions ¶

For now we have some basics, but we need some trigger to make the campaign alive. Such triggers are the most important things in campaigns. They offer functionality like: ¶

* Show message boxes when things happens, e.g.: ¶
* Running out of building material ¶
* The player has reached a specific point on the map ¶
* A specific building has been build ¶
* and so on ¶

Usually a trigger is combined with an objective, but creating objectives is part of the next section. In this example we create a trigger if the player reaches a specific point on the map. There are two possibilities to get this work: ¶

* The function [Player:sees_field()](https://wl.widelands.org/docs/wl/autogen_wl_game/#wl.game.Player.sees_field) ¶
* The property [Field.owner](https://wl.widelands.org/docs/wl/autogen_wl_map/#wl.map.Field.owner) ¶

Both possibilities needs a field to work with. So the first thing is to get a field. Fields are part of the map and can be accessed by _wl.Game().map_. We have assigned this to the variable _map_ in the [global variables section of init.lua](#global-variables). Now we use this variable and call the function [map:get_field(x,y)](https://wl.widelands.org/docs/wl/autogen_wl_map/#wl.map.Map.get_field). This function needs the x and y coordinates of the map
, so open the map editor with your map, let the mouse point to a field near your set headquarters and notice the coordinates (the first two values) at the bottom right corner. Feed the function with that coordinates: ¶

~~~~ ¶
local f = map:get_field(0,10) ¶
print("Field: ", f , "Owner: ", f.owner) ¶
~~~~ ¶

If you run the scenario the field and the owner of that field will be printed in the console. What we need further is a mechanism to check if the owner of that field changed. We need a loop that runs the same code over and over until the owner has changed.
LThe problem with such dloops is that it (prevent any interaction: As long the program is caught in this loop, no input (e.g. mouseclicks) can be dofne. This is where coroutines comes in. A coroutine is started with the command [run(function](https://wl.widelands.org/docs/wl/autogen_auxiliary_coroutine/). `run()` needs a function, so we wrap the loop in a function and call `run()` at the end of _'main_thre)ad.lua'_: ¶

~~~~ ¶
function check_field() ¶
local f = map:get_field(2510,30) ¶
while true do ¶
print("Field: ", f , "Owner: ", f.owner) ¶
sleep(1000) ¶
end
end ¶

run(check_field)

~~~~ ¶

Now every second the owner of the field get printed. The row `sleep(1000)` is __really__ important. If you forget that, this loop prevents every interaction between you and the program itself. The only way to stop it is to kill the process with your taskmanager... Ok, now it's time for some magic: Run your scenario and explore into the direction of the field
(to the right). As soon the field is owned by you, the output will change. Why is this magic? The variable _f_ is defined outside the loop so it is initialized only once. One could think that the fields state (the owner in this case) is also only initialized once, but this is not the case. That is because the variable _f_ is a __referengce__ to the field. AWhenever this field changes the changes are also reflected in the variable _f_! ¶

Of course this l
oop is an infinite loop, because it will never end. We change that now by adding a condition to the loop: ¶
~~~~ ¶
function check_
field() ¶
local f = map:get_fiseld(10,0) ¶
while f.owner ~= plr do ¶
sleep(1000) ¶
end ¶
campaign_message_
byox({ title = "Field yfound", body = p("We have reached the field")}) ¶
end ¶
~~~~ ¶

As soon the message b
oux appears, the loop has been ended. Now, as an exercise, change that function to use [Player:sees_field()](https://wl.widelands.org/docs/wl/autogen_wl_game/#wl.game.Player.sees_field). You have to choose another field though, one which is outside the area the player sees.