[Solved] Ignoring a chunk of text ?

Started by MetalZelda, April 05, 2016, 12:20:15 AM

Previous topic - Next topic
April 05, 2016, 12:20:15 AM Last Edit: April 05, 2016, 03:06:55 AM by MetalZelda
Hi.

So earlier I was imaginating a mail system and how it should work. It works properly (https://github.com/MetalES/Project-Zelda/issues/4), but I am in need of help.

The mail's text content is stored in another file (see bellow), this is to ensure that translators will not be lost and also to keep string.dat clean (Only mail names are part of string.dat)

Everything works fine, except that I need to ignore a chunk of line in order to display the right page.

This is how a mail file looks like (of course, this is a test)
$ = Tell the engine to go to another line
  • [/0] = Tell the engine that this page start and ends there (Is there a way to do it like so ?)


    [reward]heart_piece

    [0]
    Line0$
    Line1$
    Line2$
    Line3$
    Line4$
    Line5$
    Line6$
    [/0]

    [1]
    Line0$
    Line1$
    Line2$
    Line3$
    Line4$
    Line5$
    Line6$
    [/1]

    [2]
    Line0$
    Line1$
    Line2$
    Line3$
    Line4$
    Line5$
    Line6$
    [/2]


    This is the code
    Code (lua) Select

    local submenu = require("scripts/menus/pause_submenu")
    local mail_submenu = submenu:new()

    function mail_submenu:on_started()
      submenu.on_started(self)
     
      self.game:set_custom_command_effect("attack", "return")
      self.game:set_custom_command_effect("action", "open")
     
      self.cursor_sprite = sol.sprite.create("menus/pause_cursor")
      self.cursor_sprite:set_animation("letter")
     
      self.displaying_mail = false

      local font, font_size = sol.language.get_menu_font()
      local width, height = sol.video.get_quest_size()

      self.mail_name_surface = sol.surface.create(320,800)
     
      -- Todo, this is a test, rezise this to fit the page bitmap
      self.text_display_surface = sol.surface.create(320, 240)
     
      self.mail_highest_visible = 1
      self.mail_visible_y = 0
      self.mail_names = {}
     
      -- This handle the whole mail page
      self.mail_content = sol.text_surface.create{
        horizontal_alignment = "left",
        vertical_alignment = "top",
        font = "lttp",
        font_size = 14,
        text = text_lines,
      }
     
      --Todo
      self.page_number = sol.text_surface.create({
        horizontal_alignment = "left",
        vertical_alignment = "top",
        font = "lttp",
        font_size = 14,
        text = "0 /" .. self.game:get_value("total_mail") / 5,
      })
      --
     
      for i = 1, 100 do
        if self.game:get_value("mail_" .. i .. "_obtained") then
          self.mail_names[i] = sol.text_surface.create{
            horizontal_alignment = "left",
            vertical_alignment = "top",
            font = "lttp",
            font_size = 14,
            text_key = "quest_status_mail.mail." .. self.game:get_value("mail_" .. i .. "_name"),
          }
    end
      end

      self.up_arrow_sprite = sol.sprite.create("menus/arrow")
      self.up_arrow_sprite:set_direction(1)
      self.up_arrow_sprite:set_xy(58, 48)
      self.down_arrow_sprite = sol.sprite.create("menus/arrow")
      self.down_arrow_sprite:set_direction(3)
      self.down_arrow_sprite:set_xy(58,212)

      self.cursor_position = nil
      self:set_cursor_position(1)
     
      self:load_extra()
    end


    function mail_submenu:load_extra()
      self.mail_name_surface:clear()
     
      local misc_img = sol.surface.create("menus/quest_status_mail_system.png")

      -- misc_img:draw_region(0, 37, 182, 172, self.mail_surface, 129, 55)
       
      for i = 1, 100 do
        local y = 7 + (i - 1) * 32
    local x = 18

    if self.game:get_value("mail_" .. i .. "_obtained") then
      misc_img:draw_region(0, 22, 310, 32, self.mail_name_surface, 5, 0 + (33 * (i - 1)) - (i - 1))
      self.mail_names[i]:draw(self.mail_name_surface, 47, y + 2)
     
      if self.game:get_value("mail_" .. i .. "_opened") then
        misc_img:draw_region(0, 0, 27, 19, self.mail_name_surface, x, y - 1)
      elseif self.game:get_value("mail_" .. i .. "_highlighted") then
        misc_img:draw_region(26, 1, 27, 18, self.mail_name_surface, x, y)
      else
        misc_img:draw_region(52, 1, 27, 18, self.mail_name_surface, x, y)
      end
    end
      end
    end


    function mail_submenu:set_cursor_position(position)

      if position ~= self.cursor_position then

        local width, height = sol.video.get_quest_size()

        self.cursor_position = position
        while position <= self.mail_highest_visible do
          self.mail_highest_visible = self.mail_highest_visible - 1
          self.mail_visible_y = self.mail_visible_y - 32
        end

        while position > self.mail_highest_visible + 5 do
          self.mail_highest_visible = self.mail_highest_visible + 1
          self.mail_visible_y = self.mail_visible_y + 32
        end

        self.cursor_sprite.x = 10
        self.cursor_sprite.y = 40 + 32 * (position - self.mail_highest_visible)
      end
    end

    function mail_submenu:on_draw(dst_surface)
      -- Text.
      self.mail_name_surface:draw_region(0, self.mail_visible_y + 32, 320, 160, dst_surface, 0, 53) -- (0, self.mail_visible_y + 27, 320, 169, dst_surface, 17, 49

      -- Arrows.
      if self.game:get_value("total_mail") > 5 then
        if self.mail_visible_y > 0 then
          self.up_arrow_sprite:draw(dst_surface)
        end

        if self.mail_visible_y < 60 then
          self.down_arrow_sprite:draw(dst_surface)
        end
      end
     
      self.text_display_surface:draw(dst_surface, 0, 0)
        -- Cursor.
      self.cursor_sprite:draw(dst_surface, self.cursor_sprite.x, self.cursor_sprite.y)
      self:draw_save_dialog_if_any(dst_surface)
    end


    function mail_submenu:on_command_pressed(command)
      local handled = submenu.on_command_pressed(self, command)

      if not handled then
        if command == "left" then -- maybe use this for page ?
          handled = true
     
        elseif command == "right" then
          handled = true
     
        elseif command == "up" then
      if self.game:get_value("unread_mail_amount") > 1 then
        sol.audio.play_sound("menu/cursor")
    if self.cursor_position == 1 then
      self:set_cursor_position(self.game:get_value("unread_mail_amount"))
    else
      self:set_cursor_position(self.cursor_position - 1)
    end
      end
          handled = true
     
        elseif command == "down" then
      if self.game:get_value("unread_mail_amount") > 1 then
    sol.audio.play_sound("menu/cursor")
    if self.cursor_position >= self.game:get_value("unread_mail_amount") then
      self:set_cursor_position(1)
    else
      self:set_cursor_position(self.cursor_position + 1)
    end
      end
          handled = true
     
    elseif command == "pause" then
      submenu.avoid_can_save_from_qsmenu = false
      self.display_mail = false
      sol.menu.stop(self)
     
        elseif command == "action" then
      --todo

      if self.is_reading then
        self:display_mail(nil)
    self.is_reading = false
      else
        self:display_mail(self.cursor_position)
        self.is_reading = true
    if not self.game:get_value("mail_" .. self.cursor_position .. "_opened") then
      self.game:set_value("mail_" .. self.cursor_position .. "_opened", true)
    end
      end
     
          sol.audio.play_sound("danger")
      handled = true
     
        elseif command == "attack" then
      if not self.displaying_mail then
        self.game:set_custom_command_effect("action", "open")
            self.game:set_custom_command_effect("attack", "save")
        submenu.secondary_menu_started = false
            sol.timer.start(1, function()
          submenu.avoid_can_save_from_qsmenu = false
        end)
        sol.menu.stop(self)
      else
        -- if it is displaying a letter, just clear the message, it is very simple.
        self.displaying_mail = false -- that's it.
      end
          handled = true
        end
      end
     
      if not self.game:get_value("mail_" .. self.cursor_position .. "_highlighted") then
        self.game:set_value("mail_" .. self.cursor_position .. "_highlighted", true)
      end
     
      self:load_extra()
      return handled
    end

    function mail_submenu:display_mail(index)
      local mail_text
      self.current_page = 0

      if index ~= nil then
        -- Load the target file, only in read mode, the file name is the name of the letter, given by it's savegame variable.
    -- These files are part of dialogs, so they need to be part of the language system. So mails can be translated with ease.
    -- Luckily, savegame variable for this is ("mail_" next_mail_pointer_non_allowed "_" name), so you just have to get the cursor position in order to get that file
        mail_text = sol.file.open("languages/".. sol.language.get_language() .. "/text/mail/" .. self.game:get_value("mail_".. self.cursor_position .. "_name") .. ".dat", "r")
    -- Read the file, and store all of it's content in self.text.
        self.text = mail_text:read("*all")
    -- We get all of the file's text stored in self.text, the host file isn't interesting anymore, unload it.
    mail_text:close()
    -- Parse the text.
    self:parse_text()
    -- We are reading the mail, on_command_pressed need an alternate command mapping
    self.reading_mail = true
      else
        -- We are on the mail, and player pressed "close", he closed the mail, clear everything that regards this mail.
        self.text_display_surface:clear()
    -- The surface handling the text has been cleared, but not the text
    self.mail_content:set_text("")

        mail_text = nil
        self.text = nil
      end
    end

    function mail_submenu:parse_text()
      local line = 0
     
      -- Start some wizzard stuffs here

      -- $ = pass line, works
      for text in string.gmatch(self.text, "[^$]+") do
        line = line + 1
    self.mail_content:set_text(text)
        self.mail_content:draw(self.text_display_surface, 0, 0 + (line * 15))
      end
    end

    return mail_submenu


    The problem is, when opening a mail, I want to ignore all things that doesn't regard the current page , and I am not really good when it comes to string modification, I don't know how to do it, I mostly want the engine to analyse and draw texts that are between [page] and [/page].

    Any help would be welcome

April 05, 2016, 01:57:47 AM #1 Last Edit: April 05, 2016, 02:26:00 AM by Diarandor
Hi! I think this can be done using some of the Lua functions for strings. Probably the best option will be just using "string.match" (the function "string.find" could be useful too, but maybe worse). (I think "string.gmatch" is similar, but it gives an iterator to find patterns instead, if I am not wrong.) It should not be too hard (but I agree these things are annoying and too easy to forget). Fortunately there are good tutorials explaining how to get the patterns. Check the following links in the given order:

A very nice tutorial which I used once and found it greatly useful:
http://lua-users.org/wiki/PatternsTutorial

A manual about patterns which may help too:
http://www.lua.org/manual/5.2/manual.html#6.4.1

A list of Lua string functions (you do not need this now, but it may help for other purposes):
http://lua-users.org/wiki/StringLibraryTutorial

I cannot help you more since I have forgotten how to use the patterns, so I would need to study the tutorials again.

EDIT: you may also find some examples of uses of the "string.match" function in your dialog box script.

EDIT2: be careful because some of the characters you use (like "[" and "]") have special uses for the pattern syntax (they are called magic characters in the second link), as you can read in the tutorials. You can match them using "%" before them, i.e., with "%[" and "%]".
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

April 05, 2016, 02:49:14 AM #2 Last Edit: April 05, 2016, 02:52:26 AM by MetalZelda
Thanks Diarandor for the help, indeed, it is useful, but I might have find a easier way to do it using require instead of sol.load (and it just use the "$" formatting instead of a huge and long process of string manipulation

something like this also work, this is almost the same thing, but instead it use a Lua table to get the text, and it is still understandable for translators (plus, coms can be added to gave them hints since the format is lua)

This is how the mail file now looks like
Code (lua) Select
local text = {
-- [0] represent the page
[0] = (
"Header$" ..
"Line1$" ..
"Line2$" ..
"Line3$" ..
"Line4$" ..
"Line5$" ..
"Line6$" ..
"Line7$" ..
"Line8$"),
["reward"] = "heart_piece",
}

return text


It does work in game, I'm quite surprised