Skip to content

Code Example: How to create a wise monk

Enno Rehling edited this page Jan 4, 2014 · 1 revision

This page is very old, and likely no longer accurate. It represents Eressea scripting as of 2007

The following text is an example of how to create new content for Eressea. A lot of Eressea's content can be created without writing any C++, and that's primarily what I want to show off here.

Let' say I want to make a specialized monster. I could add a new race to the game, but if I only want one guy, I can derive him from a special race, the templates. I'll give him a few skills and items, too. I'll do this in a lua function that I'm adding to the main script that executes the turn.

function make_wiseman()
  -- make sure this gets done only once:
  local monsters = get_faction(0)
  local u = get_unit(atoi36("wise"))
  if u == nil then
    -- create a new unit in faction 0 at region 0,0:
    local r = get_region(0, 0)
    local u = add_unit(monsters, r)
    u:set_race("template")
    u:set_racename("monk")
    u.name = "A wise old man"
    -- give him some skills and stuff:
    u:add_item("sword", 1)
    u:add_item("plate", 1)
    u:add_item("money", 1000)
    u:set_skill("melee", 12)
  else
    -- if somebody else has a unit with id "wise", quit
    if u.faction ~= monsters then return end
  end
  -- give him intelligence:
  u:set_brain(run_wiseman)
end

That was pretty simple, and the unit will now be created and show up in the reports of people who have a unit in [0,0]. The last line needs a bit more work, it defines a "brain" for our wise man, which we write as a separate function in lua. We want him to give an item to any magician that enters the region, unless that magician's faction already got one earlier.

function run_wiseman(self):
  for u in self.region.units do
    -- check if this is a magician (u.magic is his magic school):
    if u.magic ~= nil then
      -- it is possible to read/write persistent data into an object:
      local var = u.faction:get_variable("wise:flag")
      if var is nil then
        u.faction:set_variable("wise:flag", "ok")
        u.add_item("wiseman_item", 1)
        -- tell the player what hapened:
        local msg = message("wiseman_tell")
        msg:set_unit("wiseman", self)
        msg:set_unit("region", self.region)
        msg:set_resource("item", "wiseman_item")
        msg:set_unit("target", u)
        msg:send_faction(u.faction)
      end
    end
  end
end

There are several interesting things here, but I'll only explain the bit about the message in detail. In Eressea, all text is localized. If your native language is German, you get German messages, if it's English, English messages, etc. So I can't write "A wise man (wise) in Xontormia (0,0) gives Linda (Lind) a magic scroll", at least not at this point. Instead I'm creating a message-object by name ("wiseman_tell") and passing it all the necessary info (in this case the two units and the region they are in) and sending that to the receipient's faction. To define the message type, I edit an XML file:

<message name="wiseman_tell" section="events">
<type>
    <arg name="region" type="region"/>
    <arg name="wiseman" type="unit"/>
    <arg name="target" type="unit"/>
    <arg name="item" type="resource"/>
  </type>
  <text locale="de">"In $region($region), $unit($wiseman) gives $unit($target) a $resource($item)."</text>
  <text locale="en">"$unit($wiseman) gibt in $region($region) ein $resource($item) an $unit($target)."</text>
</message>

These are two rules to construct the message in English and German, adding more languages is simple but mind-numbing work, although I've got about half of them translated to French.

The item I'm giving away is the "wiseman_item", a special item I want to create specifically for this guy. Items and all other resources are also defined in XML resources, and they look like this:

<resource name="wiseman_item">
  <item weight="1">
    <function name="use" value="lua_useitem"/>
  </item>
</resource>

The complete syntax for items is rather complex, you can for example define their abilities when used as weapons, their carrying capacity, receipies for building them and skill requirements. This one's simple. All I have defined is a function that will be called when a unit gives the USE order. Since I don't want to use one of the builtin C++ functions, I'm using lua_setitem, a builtin function that calls a function itemname_use in lua. Since we want the item to be a scroll, let's make it give the magician a new spell: The create_firesword spell:

function wiseman_item_use(u, amount)
  -- only magicians can use the item:
  if u.magic == nil then return -1 end
  -- add a new spell to the magician's spelllist:  
  u:add_spell(u, "create_firesword")
  -- the scroll destroys itself when used:
  u:add_item("wiseman_item", -1)
  return 0 -- okay!
end

But what is that spell? It's a new spell, we need to define what it does. To do so, we first add it to the XML resources. We define it as a level 12 spell that can be cast in any school, and it requires one sword, 100 aura points, 1 point of permanent aura and a berserker potion:

<spell name="create_firesword" rank="5" level="12">
  <function name="cast" value="luaspell"/>
  <resource name="aura" amount="100" cost="fixed"/>
  <resource name="berserkpotion" amount="1" cost="fixed"/>
  <resource name="sword" amount="1" cost="fixed"/>
  <resource name="permaura" amount="1" cost="fixed"/>
</spell>

Similar to the item's use function, the spell has a cast function that can call either a builtin function (for complex spells that are better written in C++) or "luaspell", for functions we want to write ourselves. We name them after the spell. Again, we're giving feedback through a localized message:

function create_firesword(r, mage, level, force)
  local msg = message("item_create_spell")
  msg:set_unit("mage", mage)
  msg:set_int("number", 1)
  msg:set_resource("item", "firesword")
  msg:send_faction(mage.faction)
  mage:add_item("firesword", 1);
  return level
end

There are still a few loose ends here. For example, we've introduced a couple of names (of new items, of spells, of a new race) as well as descriptions of the new item and spell. These are added to an XML resource file defining simple localized strings:

<strings>
  <string name="wiseman_item">
    <text locale="de">Spruchrolle</text>
    <text locale="en">magic scroll</text>
  </string>
  <string name="wiseman_item_p">
    <!-- plural form -->
    <text locale="de">Spruchrollen</text>
    <text locale="en">magic scrolls</text>
  </string>
  <string name="firesword">
    <text locale="de">Flammenschwert</text>
    <text locale="en">flaming sword</text>
  </string>
  <string name="firesword_p">
    <!-- plural form -->
    <text locale="de">Flammenschwerter</text>
    <text locale="en">flaming swords</text>
  </string>
  <namespace name="iteminfo">
    <!-- these strings are displayed when the player gives a SHOW order -->
    <string name="wiseman_item">
      <text locale="en">This scroll can be used to gain a new spell.</text>
      <text locale="de">Diese Spruchrolle verschafft ihrem Benutzer einen neuen Zauber.</text>
    </string>
  </namespace>
  <namespace name="iteminfo">
    <string name="wiseman_item">
      <text locale="en">This scroll can be used to gain a new spell.</text>
      <text locale="de">Diese Spruchrolle verschafft ihrem Benutzer einen neuen Zauber.</text>
    </string>
  </namespace>
  <namespace name="spell">
    <string name="create_firesword">
      <text locale="de">Erschaffe ein Flammenschwert</text>
      <text locale="en">Create A Flamesword</text>
    </string>
  </namespace>
  <namespace name="racename">
    <string name="monk">
      <text locale="de">Mönch</text>
      <text locale="en">monk</text>
    </string>
    <string name="monk_p">
      <!-- plural form -->
      <text locale="de">Mönche</text>
      <text locale="en">monks</text>
    </string>
  </namespace>
</strings>

There is a lot more that can be done, but I think the little monk is a good example that shows how everything is interconnected.

I hope you found it instructive, and maybe you want to immitate some of it in your own game. I can recommend it, it makes the game a lot more flexible, and it sure was fun to write.

Clone this wiki locally