Fandom

Freeciv

Events Tutorial

704pages on
this wiki
Add New Page
Talk0 Share

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.

So you want to add some events, but you have no idea how? Well you've come to the right place, because so do I. This tutorial documents my journey into the world of events (it may later be refined to be a more straightforward tutorial).

The following examples work with Freeciv 2.1 (Example 1) and Freeciv 2.2 (All examples)

The basic premise Edit

An event includes a trigger (signal) and an action. In the script code you will write, you create a callback function using the Lua scripting language. This callback function provides the actions and is linked up to a trigger that is invoked from the freeciv code itself.

An example is

signal.connect('unit_moved', 'unit_moved_callback')

which registers the script to listen for unit_moved events. unit_moved_callback should be a function in the Lua script.


Getting started Edit

You may add events either to a ruleset or to a scenario. Changing the ruleset will affect anyone who plays with that ruleset; if you add an event to the default ruleset it will affect almost everyone. Adding an event to the scenario means only someone who plays with your scenario will get this event. As an example, the civ2 ruleset is a ruleset, while a Europe or World-War-Two scenario would probably be scenarios.

To add events to the ruleset you must edit the script.lua file in the ruleset directory. This is pretty straightforward.

To add events to the scenario you must add some script code directly inside the .sav file itself. This means you add a section

[script]
code=$
...
$

to the .sav file. The code you will write goes in place of the ... bits between the $ signs.

If we insert the shortest possible event handler in such a code block, it looks like this:

[script]
code=$
-- This callback reacts when a unit is moved
function unit_moved_callback(unit, src_tile, dst_tile)
  notify.all('A/An %s moved!', unit.utype:name_translation())
end     

signal.connect('unit_moved', 'unit_moved_callback')
$

To see how this works in practice, look in the tutorial at data/scenario/tutorial.sav.gz ; the file can be uncompressed and viewed in a text editor.


Example 1: Scenario narrative Edit

Download Scenario File with Example 1

An example that could be used in the tutorial: Tell the user that a found spot is good for a city. We make a trigger for the unit_moved event and look if a Setter moves to a Plains or Grassland tile:

[script]
code=$
-- This line is a comment since it is marked with two dashes
function has_unit_type_name(unit, utype_name)
  return (unit.utype.id == find.unit_type(utype_name).id)
end
function has_tile_terrain_name(tile, terrain_name)
  return (tile.terrain.id == find.terrain(terrain_name).id)
end

-- This callback reacts when a unit is moved
function unit_moved_callback(unit, src_tile, dst_tile)
  if unit.owner:is_human() then
    if has_unit_type_name(unit, 'Settlers')
       and (has_tile_terrain_name(dst_tile, 'Grassland')
       or has_tile_terrain_name(dst_tile, 'Plains')) then
       notify.event(unit.owner, nil, E.TUTORIAL or E.SCRIPT, [[
This looks like a good place to build a city.  The next time this
unit gets a chance to move, press (b) to found a city.

In general you want to build cities on open ground near water.  Food
is the most important resource for any city.  Grassland and plains
provide plenty of food.]])
    end
  end
end
signal.connect('unit_moved', 'unit_moved_callback')
$

In this example, the special [[ .. ]] syntax denotes a multiline string. This is so we can show a long message to the user. A short string may also be written with quotes in only one line: notify.player(unit.owner, "This is a good spot for a city!")

notify.event takes four parameters here: Player to receive the message, Tile where the event happened, message category and the message itself. As you can see, we can say 'nil' (which means "empty value") to say that there is no specific tile to this event. If you want though, you extend this example by using unit.tile as the event location.

Example 2: Creating Units Edit

Download Scenario File with Example 2

In this example, we change the way we create mercenaries from huts. Instead of defining a new event handler, we override an event handler in the default ruleset (Freeciv 2.2).

In our version, we always create Archers as mercenaries, and we tell the user "Archers join your side!".

[script]
code=$
-- Define a function by the same name as in the
-- default rulset. Our Scenario version supercedes
-- the ruleset version.
function default_hut_get_mercenaries(unit)
  local owner = unit.owner
  local utype = find.unit_type("Archers")

  if utype then
    notify.event(owner, unit.tile, E.HUT_MERC,
                 "%s join your side!", utype:name_translation())
    -- create_unit takes arguments:
    -- Player, Tile, Unit Type, Veteran level, Homecity, Moves Left
    create_unit(owner, unit.tile, utype, 0, unit:get_homecity(), -1)
    return true
  else
    return false
  end
end

-- In early versions, the function used this name instead
hut_get_mercenaries = default_hut_get_mercenaries

-- NOTE: We don't use signal.connect when overriding
$

Example 3: Scenario Objective Edit

Download Scenario File with Example 3

We may use events to give scenario victory to a player according to any custom objectives. Let's create a mini-scenario with two opposing sides with each side with its own capital (here, Berlin and Paris). The scenario shall be won whenever one side conquers the capital of the other. '

[script]
code=$
-- I used the Map editor to look up the identifiers of these cities,
-- they stay constant through the game even if the cities change name
berlin_city_id = 105
paris_city_id = 106

-- React to the city_lost event
function city_was_lost(city, loser, winner)
  local napoleon = find.player(0)
  local friedrich = find.player(1)

  if city.id == paris_city_id then
    friedrich:victory()
    notify.all("%s is lost! %s wins the war.",
	       city.name, friedrich.name)
  elseif city.id == berlin_city_id then
    napoleon:victory()
    notify.all("%s is lost! %s wins the war.",
	       city.name, napoleon.name)
  end
end

function city_was_destroyed(city, loser, destroyer)
  -- in this case, we can handle it the same way
  city_was_lost(city, loser, destroyer)
end

signal.connect('city_lost', 'city_was_lost')
signal.connect('city_destroyed', 'city_was_destroyed')
$

Available Events and Event Scripting API Edit

Please read the Lua reference manual for a reference to all available events, actions and datatypes with their methods that are available in the Event Scripting API.

Also on Fandom

Random Wiki