Events Tutorial
From Freeciv
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).
Updated as of 2.1 development version post-beta4.
Contents |
[edit] The basic premise
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.
[edit] Getting started
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 events.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.
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.
[edit] An Example
An example that could be used in the tutorial: Tell the user that the found spot is good for a city
[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_terrain_name(tile, terrain_name)
return (tile.terrain.id == find.terrain(terrain_name).id)
end
-- Here we handle moved unit events
function unit_moved_callback(unit, src_tile, dst_tile)
if unit.owner:is_human() then
if has_unit_type_name(unit, 'Settlers')
and ( has_terrain_name(dst_tile, 'Grassland')
or has_terrain_name(dst_tile, 'Plains') ) then
notify.event(unit.owner, dst_tile, E.TUTORIAL,
_("This looks like a good place to build a city. The next time this\n\
unit gets a chance to move, press (b) to found a city.\n\
\n\
In general you want to build cities on open ground near water. Food\n\
is the most important resource for any city. Grassland and plains\n\
provide plenty of food."))
end
end
end
signal.connect('unit_moved', 'unit_moved_callback')
$
[edit] Scripting API
[edit] Signals
/**************************************************************************
Signals implementation.
...
A signal may have any number of Lua callback functions connected to it
at any given time.
A signal emission invokes all associated callbacks in the order they were
connected:
* A callback can stop the current signal emission, preventing the callbacks
connected after it from being invoked.
* A callback can detach itself from its associated signal.
Lua callbacks functions are able to do these via their return values.
All Lua callback functions can return a value. Example:
return false
If the value is 'true' the current signal emission will be stopped.
**************************************************************************/
Defined signal names and their callback signatures
turn_started (turn, year) unit_moved (unit, src_tile, dst_tile) city_built (city) city_growth (city, size) /* Only includes units built in cities, for now. */ unit_built (unit, city) building_built (building_type, city) /* These can happen for various reasons; the third argument gives the * reason (a simple string identifier). Example identifiers: * "pop_cost", "need_tech", "need_building", "need_special", * "need_terrain", "need_government", "need_nation", "never", * "unavailable". */ unit_cant_be_built (unit_type, city, string) building_cant_be_built (building_type, city, string) /* The third argument contains the source: "researched", "traded", * "stolen", "hut". */ tech_researched (tech_type, player, string) hut_enter (unit)
[edit] Data Types
The Scripting API has a number of fundamental Data types:
Player string name Nation_Type nation Player_ai ai int id City string name Player owner Tile tile int id Unit Unit_Type utype Player owner int homecity_id Tile tile int id Tile int nat_x int nat_y Terrain terrain int id Government int id Nation_Type int id Building_Type int build_cost int id Unit_Type int build_cost int id
[edit] Methods and functions
-- Player methods.
Player:is_human()
Player:num_cities()
Player:num_units()
-- Unit methods.
Unit:homecity()
-- Building_Type methods.
Building_Type:build_shield_cost()
Building_Type:is_wonder()
Building_Type:is_great_wonder()
Building_Type:is_small_wonder()
Building_Type:is_improvement()
-- Unit_Type methods.
Unit_Type:build_shield_cost()
Unit_Type:has_flag(flag)
Unit_Type:has_role(role)
--- find module
-- these functions are all prefixed with 'find.' when called
-- Example:
-- settler_type = find.unit_type('Settlers')
module find {
Player player (player_id)
City city (player, city_id)
Unit unit (player, unit_id)
Tile tile (nat_x, nat_y)
Government government (government_id)
Government government (name)
Nation_Type nation_type (name)
Nation_Type nation_type (nation_type_id)
Building_Type building_type (name)
Building_Type building_type (building_type_id)
Unit_Type unit_type (name)
Unit_Type unit_type (unit_type_id)
Tech_Type tech_type (name)
Tech_Type tech_type (tech_type_id)
Terrain terrain (name)
Terrain terrain (terrain_id)
}
--- signal module
module signal {
void connect(signal_name, callback_name);
}
-- notify functions
-- Notify module implementation.
-- To send messages to the player
-- Takes a formatted string as last argument
-- Example:
-- notify.player(pplayer, "Year %d", year)
module notify {
all(...)
player(player, ...)
event(player, tile, event, ...)
embassies(player, ptile, event, ...)
}
-- utilities
-- generate random integer
int random (min, max)
-- actions
Unit create_unit (player, tile, unit_type, veteran_level, homecity, moves_left);
void create_city (player, tile, name);
void change_gold (player, with_amount);
Tech_Type give_technology (player, tech_type);
