← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/fh1-winconditions into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/fh1-winconditions into lp:widelands.

Commit message:
Converted win condition scripts to new font renderer. Formatting functions for the new renderer live in data/scripting/richtext.lua

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/fh1-winconditions/+merge/323987

I decided to add a separate script for the formatting stuff. This way, we can keep the old renderer around until after the release so that savegames won't crash.

I will replace the online documentation for Richtext once we have support for campaign scenarios - I haven't touched the message boxes and objectives yet.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/fh1-winconditions into lp:widelands.
=== modified file 'data/scripting/formatting.lua'
--- data/scripting/formatting.lua	2016-10-19 09:00:29 +0000
+++ data/scripting/formatting.lua	2017-05-12 14:03:15 +0000
@@ -1,3 +1,6 @@
+-- TODO(GunChleoc): This is for the legacy font renderer. Remove when we remove the old renderer.
+
+
 -- RST
 -- formatting.lua
 -- --------------

=== added file 'data/scripting/richtext.lua'
--- data/scripting/richtext.lua	1970-01-01 00:00:00 +0000
+++ data/scripting/richtext.lua	2017-05-12 14:03:15 +0000
@@ -0,0 +1,412 @@
+-- NOCOM(GunChleoc): Wrap all tags and document allowed attributes when we're done
+-- RST
+-- formatting.lua
+-- --------------
+--
+-- Functions to simplify and unique text formatting in scenarios and help files.
+-- Most of these functions are simple wrapper functions that make working with
+-- widelands rich text formatting system more bearable.
+-- Function names generally follow HTML names.
+
+
+-- RST
+-- .. function:: localize_list(items, listtype, former_textdomain)
+--
+--    Turns an array of string items into a localized string list with
+--    appropriate concatenation.
+--
+--    e.g. localize_list({"foo", "bar", baz"}, "or", "widelands") will return
+--    _"foo, bar or baz"
+--
+--    :arg items:              An array of strings
+--    :arg listtype:           The type of concatenation to use.
+--                             Legal values are "&", "and", "or", and ";"
+--    :arg former_textdomain:  The textdomain to restore after running this function.
+--    :returns: The concatenated list string, using localized concatenation operators.
+--
+-- Same algorithm as in src/base/i18n
+function localize_list(items, listtype, former_textdomain)
+   set_textdomain("widelands")
+   local result = ""
+   for i, item in pairs(items) do
+      if (i == 1) then
+         result = item
+      elseif (i == #items) then
+         if (listtype == "&") then
+            result = _"%1$s & %2$s":bformat(result, item)
+         elseif (listtype == "or") then
+            result = _"%1$s or %2$s":bformat(result, item)
+         elseif (listtype == ",") then
+            result = _"%1$s, %2$s":bformat(result, item)
+         else
+            result = _"%1$s and %2$s":bformat(result, item)
+      end
+      else
+         result = _"%1$s, %2$s":bformat(result, item)
+      end
+   end
+   set_textdomain(former_textdomain)
+   return result
+end
+
+
+-- RST
+-- .. function:: rt(text_or_attributes[, text = nil])
+--
+--    Wraps a block of text into Lua rich text.
+--    Only call this once for the whole text that gets sent to the backend.
+--    There is no general need to wrap an rt tag around your text,
+--    because the backend will take care of it.
+--    So, only use this function if you wish to add some attributes to the tag.
+--
+--    Allowed attributes are:
+--       padding, padding_r, padding_l, padding_b, padding_t: NOCOM(GunChleoc): Document
+--       background:  a background color or image
+--       debug:       add visual debug information and debug log
+--       editor_mode: allow multiple blank spaces for editing
+--
+--    :arg attributes: the attributes for the rt tag.
+--    :type attributes: :class:`string`
+--    :arg text: the text to be enclosed in rich text tags.
+--    :type text: :class:`string`
+--    :returns: the wrapped rich text.
+--
+function rt(text_or_attributes, text)
+   if text then
+      return "<rt " .. text_or_attributes .. ">" .. text .. "</rt>"
+   else
+      return "<rt>" .. text_or_attributes .. "</rt>"
+   end
+end
+
+
+-- RST
+-- .. function:: img(src[, attributes = nil])
+--
+--    Turns an image src path into an image tag for richtext.
+--
+--    :arg src: the file path to the image.
+--    :type src: :class:`string`
+--    :arg attributes: the attributes for the div tag.
+--    :type attributes: :class:`string`
+--
+--    Allowed attributes are:
+--       color:     a hex color to be applied to the image's player color mask, if it has any
+--       ref:       NOCOM(GunChleoc): I have no idea what it does.
+--
+--    :returns: the img tag.
+--
+function img(src, attributes)
+   if attributes then
+      return "<img src=" .. src .." " .. attributes .. ">"
+   else
+      return "<img src=" .. src .. ">"
+   end
+end
+
+
+function title(font_face, text)
+   return p_font("align=center", "size=38 face=".. font_face .. " color=2F9131", text)
+end
+
+
+-- RST
+-- .. function:: h1(text_or_color[, text = nil])
+--
+--    Returns a paragraph formatted as a big heading with a small gap after it.
+--    The mnemonic comes from HTML.
+--
+--    :returns: A paragraph with text formatted as heading.
+function h1(text_or_color, text)
+   if text then
+      return p_font("", "size=18 bold=1 color=".. text_or_color, vspace(6) .. text .. vspace(1))
+   else
+      return p_font("", "size=18 bold=1 color=D1D1D1", vspace(6) .. text_or_color .. vspace(1))
+   end
+end
+
+-- RST
+-- .. function:: h2(text)
+--
+--    Like :func:`h1` but smaller.
+--
+--    :returns: A paragraph with text formatted as heading.
+function h2(text)
+   return p_font("", "size=14 bold=1 color=D1D1D1", vspace(6) .. text .. vspace(1))
+end
+
+-- RST
+-- .. function:: h3(text)
+--
+--    Like :func:`h2` but smaller.
+--
+--    :returns: A paragraph with text formatted as heading.
+--
+function h3(text)
+   return p_font("", "size=13 color=D1D1D1", vspace(4) .. text .. vspace(1))
+end
+
+-- RST
+-- .. function:: h4(text)
+--
+--    Like :func:`h3` but smaller.
+--
+--    :returns: A paragraph with text formatted as heading.
+--
+function h4(text)
+   return p_font("", "size=12 italic=1 color=D1D1D1", text)
+end
+
+-- RST
+-- .. function:: p(text_or_attributes[, text = nil])
+--
+--    Returns one paragraph with text followed by a small vertical gap. Options
+--    can be given as first argument similar to :func:`rt`.
+--
+--    Allowed attributes are documented in the open_p function.
+--
+--    :returns: The text wrapped in <p>%s</p>
+function p(text_or_attributes, text)
+   if text then
+      return open_p(text_or_attributes) .. text .. close_p()
+   else
+      return open_p() .. text_or_attributes .. close_p()
+   end
+end
+
+
+-- RST
+-- .. function:: p(text_or_attributes[, text = nil])
+--
+--    Returns one paragraph with text followed by a small vertical gap. Options
+--    can be given as first argument similar to :func:`rt`.
+--
+--    Allowed p attributes are documented in the open_p function.
+--
+--    Allowed font attributes are are documented in the font function.
+--
+--    :returns: The text wrapped in <p attributes><font attributes>text</font></p>
+function p_font(p_or_font_or_attributes, text_or_font_attributes, text)
+   if text then
+      return ("<p %s>"):format(p_or_font_or_attributes) .. "<font " .. text_or_font_attributes .. ">" .. text .. close_p()
+   else
+      return "<p><font " .. p_or_font_or_attributes .. ">" .. text_or_font_attributes .. close_p()
+   end
+end
+
+
+-- RST
+-- .. function:: open_p([attributes = nil])
+--
+--    Returns a paragraph open tag and default font size. Options
+--    can be given as first argument similar to :func:`rt`.
+--
+--    Allowed attributes are:
+--       indent:  indents the first line of the paragraph
+--       align:   horizontal alignment (left, center, right)
+--       valign:  vertical alignment (top, middle, bottom)
+--       spacing: line spacing in pixels
+--
+--    :returns: <p> with added attributes and default font
+function open_p(attributes)
+   if attributes then
+      return ("<p %s>"):format(attributes) .. "<font size=12>"
+   else
+      return "<p><font size=12>"
+   end
+end
+
+
+-- RST
+-- .. function:: close_p(t)
+--
+--    Closes a paragraph.
+--
+--    :returns: The closing tags for a paragraph
+function close_p(t)
+   return vspace(6) .. "</font></p>"
+end
+
+-- RST
+-- .. function:: font(attributes, text)
+--
+--    Wraps the text in font tags.
+--
+--    Allowed attributes are:
+--       size:      the font size in pt
+--       face:      sans, serif or condensed
+--       color:     a hex color
+--       bold:      if you add bold=1, the text will be bold
+--       italic:    if you add italic=1, the text will be italic
+--       underline: if you add underline=1, the text will be underlined
+--       shadow:    if you add shadow=1, the text will have a shadow
+--       ref:       NOCOM(GunChleoc): I don't know what this does.
+--
+--    :returns: The text wrapped in font tags with the given attributes
+--
+function font(attributes, text)
+   return ("<font %s>"):format(attributes) .. text .. "</font>"
+end
+
+-- RST
+-- .. function:: space(gap)
+--
+--    Adds a horizontal space
+--    :arg gap: the size of the space as pixels.
+--
+--    :returns: a space tag
+--
+function space(gap)
+   return "<space gap="..gap..">"
+end
+
+-- RST
+-- .. function:: vspace(gap)
+--
+--    Adds a vertical space
+--    :arg gap: the size of the space as pixels.
+--
+--    :returns: a vspace tag
+--
+function vspace(gap)
+   return "<vspace gap="..gap..">"
+end
+
+-- RST
+-- .. function:: dl(dt, dd)
+--
+-- This function imitates a HTML description list
+--    :arg dt: "description term", will be rendered in bold.
+--    :arg dd: "description data", will be rendered normally.
+--
+--    :returns: a p tag containing the formatted text
+--
+function dl(dt, dd)
+   return p(b(dt) .. " " .. dd)
+end
+
+-- RST
+-- .. function:: li(text_or_symbol[, text = nil])
+--
+--    Adds the symbol in front of the text to create a list item and
+--    wraps it in a paragraph
+--
+--    :arg symbol: the item symbol for the list, e.g. "•" or "→"
+--    :arg text: the text of the list item
+--
+--    :returns: a p tag containint the formatted text
+function li(text_or_symbol, text)
+   if text then
+      return p(text_or_symbol .. " " .. text .. vspace(6))
+   else
+      return p("• " .. text_or_symbol .. vspace(6))
+   end
+end
+
+-- RST
+-- .. function:: li_arrow(text)
+--
+--    Creates a list item with an arrow
+--
+--    :arg text: the text of the list item
+--
+--    :returns: li("→", text)
+function li_arrow(text)
+   -- TODO(GunChleoc): Reverse arrow for rtl languages.
+   return li("→", text)
+end
+
+-- RST
+-- .. function li_image(imagepath, text)
+--
+--    Places a paragraph of text to the right of an image
+
+--    :arg imagepath: the full path to the image file
+--    :arg text_width_percent: the percentatge of space that the text will occupy
+--    :arg text: the text to be placed next to the image
+--
+--    :returns: the text wrapped in a paragraph and placed next to the image, The outer tag is a div.
+function li_image(imagepath, text_width_percent, text)
+   return p("<br>") .. div("width=100%", "") ..
+         div(p(img(imagepath))) ..
+         div(p(space(6))) ..
+         div("width="..text_width_percent.."%", p(text)) ..
+         div("width=100%", "")
+end
+
+-- RST
+-- .. function:: a(link)
+--
+-- This function imitates a HTML link. We can't do real links yet, so the text just gets underlines.
+--    :arg link: the text to format
+--
+--    :returns: a font tag containing the underlined text
+--
+function a(link)
+   return font("underline=1", link)
+end
+
+-- RST
+-- .. function:: b(link)
+--
+-- This makes the text bold.
+--    :arg link: the text to format
+--
+--    :returns: a font tag containing the bold text
+--
+function b(text)
+   return font("bold=1", text)
+end
+
+-- RST
+-- .. function:: i(link)
+--
+-- This makes the text italic.
+--    :arg link: the text to format
+--
+--    :returns: a font tag containing the italic text
+--
+function i(text)
+   return font("italic=1", text)
+end
+
+-- RST
+-- .. function:: u(link)
+--
+-- This underlines the text.
+--    :arg link: the text to format
+--
+--    :returns: a font tag containing the underlined text
+--
+function u(text)
+   return font("underline=1", text)
+end
+
+-- RST
+-- .. function:: div(text_or_attributes[, text = nil])
+--
+--    Wraps a block of text into a div tag.
+--
+--    :arg attributes: the attributes for the div tag.
+--    :type attributes: :class:`string`
+--
+--    Allowed attributes are:
+--       padding, padding_r, padding_l, padding_b, padding_t: NOCOM(GunChleoc): Document
+--       margin:     NOCOM(GunChleoc): Document
+--       float:      NOCOM(GunChleoc): this does not work yet
+--       margin:     inner margin for the div
+--       valign:     vertical alignment
+--       background: a background color or image
+--       width:      the width of the div in pixels or percent
+--
+--    :arg text: the text to be enclosed in div tags.
+--    :type text: :class:`string`
+--    :returns: the text wrapped in a div tag.
+--
+function div(text_or_attributes, text)
+   if text then
+      return ("<div %s>"):format(text_or_attributes) .. text .. "</div>"
+   else
+      return ("<div>") .. text_or_attributes .. "</div>"
+   end
+end

=== modified file 'data/scripting/win_conditions/artifacts.lua'
--- data/scripting/win_conditions/artifacts.lua	2016-09-20 17:01:35 +0000
+++ data/scripting/win_conditions/artifacts.lua	2017-05-12 14:03:15 +0000
@@ -4,7 +4,6 @@
 
 include "scripting/coroutine.lua" -- for sleep
 include "scripting/win_conditions/win_condition_functions.lua"
-include "scripting/formatting.lua"
 
 set_textdomain("win_conditions")
 
@@ -53,7 +52,7 @@
       local plrs = wl.Game().players
       if #artifact_fields == 0 then
          for idx, plr in ipairs(plrs) do
-            send_message(plr, _"No Artifacts", rt(p(_"There are no artifacts on this map. This should not happen. Please file a bug report on https://launchpad.net/widelands and specify your Widelands version and the map you tried to load.")), {popup = true})
+            send_message(plr, _"No Artifacts", p(_"There are no artifacts on this map. This should not happen. Please file a bug report on https://launchpad.net/widelands and specify your Widelands version and the map you tried to load."), {popup = true})
          end
          return
       end
@@ -61,17 +60,17 @@
       local found_artifact = {
          -- TRANSLATORS: Keep this as short as possible. You can also translate this as "New artifact"
          title = _"Artifact Found",
-         body = rt(p(_[[Your team found a new artifact.]]))
+         body = p(_[[Your team found a new artifact.]])
       }
       local lost_artifact = {
          -- TRANSLATORS: Keep this as short as possible.
          title = _"Artifact Lost",
-         body = rt(p(_[[One of your team’s artifacts was stolen by an enemy.]]))
+         body = p(_[[One of your team’s artifacts was stolen by an enemy.]])
       }
       local stole_artifact = {
          -- TRANSLATORS: Keep this as short as possible.
          title = _"Artifact Conquered",
-         body = rt(p(_[[Your team stole an artifact from an enemy.]]))
+         body = p(_[[Your team stole an artifact from an enemy.]])
       }
 
       local function _broadcast_to_team(player, msg, f)
@@ -174,10 +173,10 @@
          local key = _getkey(plr)
          -- If two or more teams have the same amount of artifacts, they are all considered winners.
          if artifacts_per_team[key] == max_artifacts then
-            plr:send_message(won_game_over.title, rt(won_game_over.body .. msg))
+            plr:send_message(won_game_over.title, won_game_over.body .. msg)
             wl.game.report_result(plr, 1, make_extra_data(plr, wc_descname, wc_version, {score=artifacts_per_team[key]}))
          else
-            plr:send_message(lost_game_over.title, rt(lost_game_over.body .. msg))
+            plr:send_message(lost_game_over.title, lost_game_over.body .. msg)
             wl.game.report_result(plr, 0, make_extra_data(plr, wc_descname, wc_version, {score=artifacts_per_team[key]}))
          end
       end

=== modified file 'data/scripting/win_conditions/collectors.lua'
--- data/scripting/win_conditions/collectors.lua	2016-12-01 21:14:28 +0000
+++ data/scripting/win_conditions/collectors.lua	2017-05-12 14:03:15 +0000
@@ -4,8 +4,6 @@
 
 include "scripting/coroutine.lua" -- for sleep
 include "scripting/messages.lua"
-include "scripting/formatting.lua"
-include "scripting/format_scenario.lua"
 include "scripting/table.lua"
 include "scripting/win_conditions/win_condition_functions.lua"
 
@@ -108,7 +106,7 @@
             plr:get_buildings(plr.tribe_name .. "_headquarters"), plr:get_buildings(plr.tribe_name .. "_warehouse"), plr:get_buildings(plr.tribe_name .. "_port")
          )
 
-         descr = descr .. h2((_"Status for %s"):format(plr.name)) .. "<p line-spacing=3 font-size=12>"
+         descr = descr .. h2((_"Status for %s"):format(plr.name))
          local points = 0
          for idx, ware in ipairs(point_table[plr.tribe_name .. "_order"]) do
             local value = point_table[plr.tribe_name][ware]
@@ -121,11 +119,11 @@
 
             local warename = wl.Game():get_ware_description(ware).descname
             -- TRANSLATORS: For example: 'gold (3 P) x 4 = 12 P', P meaning 'Points'
-            descr = descr .. listitem_bullet(_"%1$s (%2$i P) x %3$i = %4$i P"):bformat(
+            descr = descr .. li(_"%1$s (%2$i P) x %3$i = %4$i P"):bformat(
                warename, value, count, lpoints
             )
          end
-         descr = descr .. "</p>" .. h3(ngettext("Total: %i point", "Total: %i points", points)):format(points)
+         descr = descr .. h3(ngettext("Total: %i point", "Total: %i points", points)):format(points)
          team_points = team_points + points
       end
 
@@ -135,10 +133,10 @@
 
    -- Send all players the momentary game state
    local function _send_state(remaining_time, plrs)
-      local msg = ""
+      local msg = vspace(8)
       set_textdomain("win_conditions")
       if remaining_time <= 0 then
-         msg = p(_"The game has ended.")
+         msg = p(_"The game has ended.") .. vspace(8)
       else
          local h = math.floor(remaining_time / 60)
          local m = remaining_time % 60
@@ -157,14 +155,14 @@
             end
          end
          -- TRANSLATORS: Context: 'The game will end in 2 hours and 30 minutes.'
-         msg = p(_"The game will end in %s."):format(time)
+         msg = p(_"The game will end in %s."):format(time) .. vspace(8)
       end
 
       -- Points for players without team
       for idx, plr in ipairs(plrs) do
          if (plr.team == 0) then
             local points, pstat = _calc_points({plr})
-            msg = msg .. "<p font-size=8> <br></p>" .. pstat
+            msg = msg .. vspace(8) .. pstat
          end
       end
       -- Team points
@@ -173,11 +171,11 @@
          local message = h1((_"Status for Team %d"):format(idx))
             .. pstat
             .. h2(ngettext("Team Total: %i point", "Team Total: %i points", points)):format(points)
-         msg = msg .. "<p font-size=8> <br></p>" .. message
+         msg = msg .. vspace(8) .. message
       end
 
       for idx, plr in ipairs(plrs) do
-         send_message(plr, game_status.title,  "<rt>" .. msg .. "</rt>", {popup = true})
+         send_message(plr, game_status.title, msg, {popup = true})
       end
    end
 
@@ -201,10 +199,10 @@
          local lost_or_won = 0
          if (info[2] < win_points) then
             lost_or_won = 0
-            player:send_message(lost_game_over.title, rt(lost_game_over.body))
+            player:send_message(lost_game_over.title, lost_game_over.body)
          else
             lost_or_won = 1
-            player:send_message(won_game_over.title, rt(won_game_over.body))
+            player:send_message(won_game_over.title, won_game_over.body)
          end
          if (player.team == 0) then
             wl.game.report_result(player, lost_or_won, make_extra_data(player, wc_descname, wc_version, {score=info[2]}))

=== modified file 'data/scripting/win_conditions/territorial_lord.lua'
--- data/scripting/win_conditions/territorial_lord.lua	2016-09-17 11:29:34 +0000
+++ data/scripting/win_conditions/territorial_lord.lua	2017-05-12 14:03:15 +0000
@@ -3,7 +3,6 @@
 -- =======================================================================
 
 include "scripting/coroutine.lua" -- for sleep
-include "scripting/formatting.lua"
 include "scripting/messages.lua"
 include "scripting/table.lua"
 include "scripting/win_conditions/win_condition_functions.lua"
@@ -155,16 +154,14 @@
          if candidateisteam then
             candidate = (_"Team %i"):format(currentcandidate)
          end
-         local msg1 = (_"%s owns more than half of the map’s area."):format(candidate)
-         msg1 = msg1 .. "\n"
-         msg1 = msg1 .. (ngettext("You’ve still got %i minute to prevent a victory.",
+         local msg1 = p(_"%s owns more than half of the map’s area."):format(candidate)
+         msg1 = msg1 .. p(ngettext("You’ve still got %i minute to prevent a victory.",
                    "You’ve still got %i minutes to prevent a victory.",
                    remaining_time / 60))
                :format(remaining_time / 60)
 
-         local msg2 = _"You own more than half of the map’s area."
-         msg2 = msg2 .. "\n"
-         msg2 = msg2 .. (ngettext("Keep it for %i more minute to win the game.",
+         local msg2 = p(_"You own more than half of the map’s area.")
+         msg2 = msg2 .. p(ngettext("Keep it for %i more minute to win the game.",
                    "Keep it for %i more minutes to win the game.",
                    remaining_time / 60))
                :format(remaining_time / 60)
@@ -172,9 +169,9 @@
          for idx, player in ipairs(plrs) do
             if candidateisteam and currentcandidate == player.team
                or not candidateisteam and currentcandidate == player.name then
-               send_message(player, game_status.title, rt(p(msg2)), {popup = true})
+               send_message(player, game_status.title, msg2, {popup = true})
             else
-               send_message(player, game_status.title, rt(p(msg1)), {popup = true})
+               send_message(player, game_status.title, msg1, {popup = true})
             end
          end
       end
@@ -201,10 +198,10 @@
                p.see_all = 1
                if candidateisteam and currentcandidate == p.team
                   or not candidateisteam and currentcandidate == p.name then
-                  p:send_message(won_game_over.title, rt(won_game_over.body))
+                  p:send_message(won_game_over.title, won_game_over.body)
                   wl.game.report_result(p, 1, make_extra_data(p, wc_descname, wc_version, {score=_landsizes[p.number]}))
                else
-                  p:send_message(lost_game_over.title, rt(lost_game_over.body))
+                  p:send_message(lost_game_over.title, lost_game_over.body)
                   wl.game.report_result(p, 0, make_extra_data(p, wc_descname, wc_version, {score=_landsizes[p.number]}))
                end
             end

=== modified file 'data/scripting/win_conditions/territorial_time.lua'
--- data/scripting/win_conditions/territorial_time.lua	2016-09-17 11:29:34 +0000
+++ data/scripting/win_conditions/territorial_time.lua	2017-05-12 14:03:15 +0000
@@ -6,7 +6,6 @@
 -- here. Pull that out into a separate script and reuse.
 
 include "scripting/coroutine.lua" -- for sleep
-include "scripting/formatting.lua"
 include "scripting/messages.lua"
 include "scripting/table.lua"
 include "scripting/win_conditions/win_condition_functions.lua"
@@ -179,10 +178,9 @@
       local function _status(points, has_had)
          local msg = ""
          for i=1,#points do
-            msg = msg .. "\n"
             if (has_had == "has") then
                msg = msg ..
-                  listitem_bullet(
+                  li(
                      (wc_has_territory):bformat(
                         points[i][1],
                         _percent(points[i][2], #fields),
@@ -190,7 +188,7 @@
                         #fields))
             else
                msg = msg ..
-                  listitem_bullet(
+                  li(
                      (wc_had_territory):bformat(
                         points[i][1],
                         _percent(points[i][2], #fields),
@@ -204,17 +202,15 @@
 
       local function _send_state(points)
          set_textdomain("win_conditions")
-         local msg1 = (_"%s owns more than half of the map’s area."):format(currentcandidate)
-         msg1 = msg1 .. "<br>"
-         msg1 = msg1 .. (ngettext("You’ve still got %i minute to prevent a victory.",
+         local msg1 = p(_"%s owns more than half of the map’s area."):format(currentcandidate)
+         msg1 = msg1 .. p(ngettext("You’ve still got %i minute to prevent a victory.",
                    "You’ve still got %i minutes to prevent a victory.",
                    remaining_time // 60))
                :format(remaining_time // 60)
          msg1 = p(msg1)
 
-         local msg2 = _"You own more than half of the map’s area."
-         msg2 = msg2 .. "<br>"
-         msg2 = msg2 .. (ngettext("Keep it for %i more minute to win the game.",
+         local msg2 = p(_"You own more than half of the map’s area.")
+         msg2 = msg2 .. p(ngettext("Keep it for %i more minute to win the game.",
                    "Keep it for %i more minutes to win the game.",
                    remaining_time // 60))
                :format(remaining_time // 60)
@@ -225,9 +221,9 @@
             if remaining_time < remaining_max_time and _maxpoints(points) > ( #fields / 2 ) then
                if candidateisteam and currentcandidate == team_str:format(pl.team)
                   or not candidateisteam and currentcandidate == pl.name then
-                  msg = msg .. msg2 .. "\n\n"
+                  msg = msg .. msg2 .. vspace(8)
                else
-                  msg = msg .. msg1 .. "\n\n"
+                  msg = msg .. msg1 .. vspace(8)
                end
                -- TRANSLATORS: Refers to "You own more than half of the map’s area. Keep it for x more minute(s) to win the game."
                msg = msg .. p((ngettext("Otherwise the game will end in %i minute.",
@@ -240,10 +236,8 @@
                             remaining_max_time // 60))
                   :format(remaining_max_time // 60))
             end
-            msg = msg .. "\n\n"
-            msg = msg .. "</rt>" .. rt(game_status.body) .. "<rt>"
-            msg = msg .. _status(points, "has")
-            send_message(pl, game_status.title, rt(msg), {popup = true})
+            msg = msg .. vspace(8) .. game_status.body .. _status(points, "has")
+            send_message(pl, game_status.title, msg, {popup = true})
          end
       end
 
@@ -295,10 +289,10 @@
          for i=1,#points do
             if points[i][1] == team_str:format(pl.team) or points[i][1] == pl.name then
                if points[i][2] >= maxpoints then
-                  pl:send_message(won_game_over.title, wonmsg .. rt(_status(points, "had")))
+                  pl:send_message(won_game_over.title, wonmsg .. _status(points, "had"))
                   wl.game.report_result(pl, 1, make_extra_data(pl, wc_descname, wc_version, {score=_landsizes[pl.number]}))
                else
-                  pl:send_message(lost_game_over.title, lostmsg .. rt(_status(points, "had")))
+                  pl:send_message(lost_game_over.title, lostmsg .. _status(points, "had"))
                   wl.game.report_result(pl, 0, make_extra_data(pl, wc_descname, wc_version, {score=_landsizes[pl.number]}))
                end
             end

=== modified file 'data/scripting/win_conditions/win_condition_functions.lua'
--- data/scripting/win_conditions/win_condition_functions.lua	2016-03-15 08:42:41 +0000
+++ data/scripting/win_conditions/win_condition_functions.lua	2017-05-12 14:03:15 +0000
@@ -1,3 +1,4 @@
+include "scripting/richtext.lua"
 include "scripting/messages.lua"
 
 -- RST

=== modified file 'data/scripting/win_conditions/win_condition_texts.lua'
--- data/scripting/win_conditions/win_condition_texts.lua	2016-09-20 17:01:35 +0000
+++ data/scripting/win_conditions/win_condition_texts.lua	2017-05-12 14:03:15 +0000
@@ -1,13 +1,13 @@
-include "scripting/formatting.lua"
+include "scripting/richtext.lua"
 
 won_game = {
   title = _"Congratulations!",
-  body = rt(p(_"You have won this game!"))
+  body = p(_"You have won this game!")
 }
 
 lost_game = {
   title = _"You are defeated!",
-  body = rt(p(_"You lost your last warehouse and are therefore defeated. You may continue as spectator if you want."))
+  body = p(_"You lost your last warehouse and are therefore defeated. You may continue as spectator if you want.")
 }
 
 won_game_over = {
@@ -23,5 +23,5 @@
 game_status = {
   title = _"Status",
   -- TRANSLATORS: This is an overview for all players.
-  body = h3(_"Player overview:")
+  body = h2(_"Player overview:")
 }

=== modified file 'data/scripting/win_conditions/wood_gnome.lua'
--- data/scripting/win_conditions/wood_gnome.lua	2016-12-18 17:02:44 +0000
+++ data/scripting/win_conditions/wood_gnome.lua	2017-05-12 14:03:15 +0000
@@ -3,7 +3,6 @@
 -- =======================================================================
 
 include "scripting/coroutine.lua" -- for sleep
-include "scripting/formatting.lua"
 include "scripting/table.lua"
 include "scripting/win_conditions/win_condition_functions.lua"
 
@@ -92,14 +91,14 @@
       local playerpoints = _calc_points()
       local msg = p(ngettext("The game will end in %i minute.", "The game will end in %i minutes.", remaining_time))
             :format(remaining_time)
-      msg = msg .. "<p font-size=8> <br></p>" .. game_status.body
+      msg = msg .. vspace(8) .. game_status.body
       for idx,plr in ipairs(plrs) do
          local trees = (ngettext ("%i tree", "%i trees", playerpoints[plr.number]))
                :format(playerpoints[plr.number])
          -- TRANSLATORS: %1$s = player name, %2$s = x tree(s)
          msg = msg .. p(_"%1$s has %2$s at the moment."):bformat(plr.name,trees)
       end
-      broadcast(plrs, game_status.title, rt(msg))
+      broadcast(plrs, game_status.title, msg)
    end
 
    -- Start a new coroutine that checks for defeated players
@@ -143,14 +142,14 @@
    end
    table.sort(points, function(a,b) return a[2] < b[2] end)
 
-   local msg = "<p font-size=8> <br></p>" .. game_status.body
+   local msg = vspace(8) .. game_status.body
    for idx,plr in ipairs(plrs) do
-      msg = msg .. "<p font-size=8> <br></p>"
+      msg = msg .. vspace(8)
       local trees = (ngettext ("%i tree", "%i trees", playerpoints[plr.number])):format(playerpoints[plr.number])
       -- TRANSLATORS: %1$s = player name, %2$s = x tree(s)
       msg = msg ..  p(_"%1$s had %2$s."):bformat(plr.name,trees)
    end
-   msg = msg .. "<p font-size=8> <br></p>"
+   msg = msg .. vspace(8)
    local trees = (ngettext ("%i tree", "%i trees", playerpoints[points[#points][1].number]))
          :format(playerpoints[points[#points][1].number])
    -- TRANSLATORS: %1$s = player name, %2$s = x tree(s)
@@ -160,12 +159,12 @@
    for i=1,#points-1 do
       privmsg = lost_game_over.body
       privmsg = privmsg .. msg
-      points[i][1]:send_message(lost_game_over.title, rt(privmsg))
+      points[i][1]:send_message(lost_game_over.title, privmsg)
       wl.game.report_result(points[i][1], 0, make_extra_data(points[i][1], wc_descname, wc_version, {score=points[i][2]}))
    end
    privmsg = won_game_over.body
    privmsg = privmsg .. msg
-   points[#points][1]:send_message(won_game_over.title, rt(privmsg))
+   points[#points][1]:send_message(won_game_over.title, privmsg)
    wl.game.report_result(points[#points][1], 1,
       make_extra_data(points[#points][1], wc_descname, wc_version, {score=points[#points][2]}))
 end


Follow ups