Freeciv
Register
Advertisement

Event that happens only once[]

You may use the turn callback and react to a particular turn / year number for a unique event

function begin_ancient_era()
  -- Event that happens at 2000 BC here
end

function turn_cb(turn, year)
  if year == -2000 then
    begin_ancient_era()
  end
end
signal.connect('turn_started', 'turn_cb')

You can save a flag if an event was already performed

function city_was_lost(city, loser, winner)
  if winner:is_human() and not did_win_first_city then
     -- remember that we did this
     did_win_first_city = true
     -- now handle the event
     notify.player(winner, "Congratulations, you have taken your first city")
  end
end
signal.connect('city_lost', 'city_was_lost')
-- NOTE: We don't use the 'local' word in front of the did_win_first_city variable, because this variable
-- is a global variable (will be saved in the savegame)

Equality test[]

As of Freeciv 2.2, you may not use the equality operator == to directly compare API objects such as players, cities, units etc. Instead you must compare objects by the id field or rule name (if applicable).

For example:

-- let us pretend we know that id #110 is the city of Paris
paris_city_id = 110
function city_was_lost(city, loser, winner)
  -- see if Paris was lost
  if city.id == paris_city_id then
    notify.all("Paris lost!")
  end
  -- NOTE: we can't use  city == find.city(paris_city_id)
end

function unit_was_moved(unit, src_tile, dst_tile)
  if unit.utype:rule_name() == "Archers" then
    -- handle archers moved
  end
  -- NOTE: we can't use   unit.utype == find.unit_type("Archers")
end

Terrain Class[]

You can use a lookup table to check which class of terrain a tile has

function unit_was_moved(unit, src_tile, dst_tile)
  local bad_terrain = {
    Glacier = true,
    Desert = true,
  }
  
  local oceanic_terrain = {
    Ocean = true,
    Lake = true,
    ["Deep Ocean"] = true,
  }

  if bad_terrain[dst_tile.terrain:rule_name()] then
    notify.player(unit.owner, "This place is not so welcoming")  
  end
end

Iteration[]

It can be practical to go through every object of a kind in a scenario script. Freeciv includes lookup functions by default in the API but no looping constructs (As of Freeciv 2.2). Here is how to define looping constructs for iteration in scripts.

The following code defines

  • players_iterate
  • whole_map_iterate
  • unit_types_iterate
  • cities_iterate
  • units_iterate

Example use:

local nunits = 0
for player in players_iterate() do
  nunits = nunits + player:num_units()
end
notify.all("There are " .. nunits .. " units in the game")

Implementation: (to be copied into your scenario or ruleset script)


do
  -- iterate over the values returned by lookup
  -- until nil is returned:
  -- lookup(0), lookup(1), lookup(2), etc
  local function index_iterate(lookup)
    local index = -1
    local function iterator(lookup)
      index = index + 1
      return lookup(index)
    end
    return iterator, lookup
  end

  -- Iterate over all players of the game
  -- NOTE: This function needs Freeciv 2.1 or 2.2.1 (not 2.2.0) due to bugs in find.player
  -- In 2.2.0, it returns all 32 players of the game.
  function players_iterate()
    return index_iterate(find.player)
  end

  -- Iterate over all tiles of the game
  -- NOTE: This function needs Freeciv 2.2.1 due to bugs in find.tile
  function whole_map_iterate()
    return index_iterate(find.tile)
  end

  function unit_types_iterate()
    return index_iterate(find.unit_type)
  end
  -- NOTE: Identical further definitions can be made for
  -- governments, tech_types, building_types etc
end


do
  -- Iterate over a player objects (cities or units)
  -- lookup function takes two arguments: lookup(player, index)
  local function player_object_iterate(player, nobjs, lookup)
    local found_objs = 0
    local index = -1
    local function iterator()
      if found_objs == nobjs then
        return nil
      end
      local obj = nil
      repeat
        index = index + 1
        obj = lookup(player, index)
      until obj ~= nil
      found_objs = found_objs + 1
      return obj
    end
    return iterator, nil
  end

  -- Iterate over all cities of 'player'
  -- if player is nil, iterate over all cities
  function cities_iterate(player)
    local ncities = 0
    if player then
      ncities = player:num_cities()
    else
      for plr in players_iterate() do
        ncities = ncities + plr:num_cities()
      end
    end
    return player_object_iterate(player, ncities, find.city)
  end

  -- Iterate over all units of 'player'
  -- if player is nil, iterate over all units
  function units_iterate(player)
    local nunits = 0
    if player then
      nunits = player:num_units()
    else
      for plr in players_iterate() do
        nunits = nunits + plr:num_units()
      end
    end
    return player_object_iterate(player, nunits, find.unit)
  end
end
Advertisement