Author Topic: Displaying an image on pause  (Read 901 times)

Max

  • Full Member
  • ***
  • Posts: 155
    • View Profile
Displaying an image on pause
« on: February 27, 2018, 04:32:05 pm »
Hey all, sorry to kind of flood the board with questions. I've been saving them up for a year trying to figure them out myself first but there's a few I need help with.

So, I've been trying to learn Solarus and lua coding for a year and a half, and I still can't figure out how to create a pause menu. I've basically given up and accepted this as a design limitation to constrain myself with, so my game only has two items. But anyway, all my playtesters have given me the feedback that with all the side quests in my game, they'd really like a main quest log kind of feature because they forget what they were supposed to be doing. I think that just displaying the current stage of the main quest on the pause screen is the way to go.

So from studying the various solarus team scripts, I think that the way to show an image would be something like this:
Code: Lua
  1. function game:on_paused()
  2.   local pause_infra_img = sol.surface.create("hud/pause_infra.png")
  3.   local pause_surface = sol.surface.create()
  4.   pause_infra_img:draw(pause_surface)
  5. end
  6.  

However, I'm really confused about how all this works. It seems like you can create a surface, but it won't be immediately drawn? So you have to draw it, but you need a surface to draw it on. But wouldn't the second surface need to be drawn on a surface too? Ahh!

MetalZelda

  • Hero Member
  • *****
  • Posts: 551
    • View Profile
Re: Displaying an image on pause
« Reply #1 on: February 27, 2018, 06:31:26 pm »
That's a very simple way

But the best way to display a pause menu, is by doing a menu
You can look at how it is done on Mystery of Solarus DX, that's mostly how I've learned to use Solarus

Max

  • Full Member
  • ***
  • Posts: 155
    • View Profile
Re: Displaying an image on pause
« Reply #2 on: February 27, 2018, 08:21:01 pm »
I'm okay with doing it the simple way at first, haha. I've looked through the code for ZS:DX, which is why I think the code I wrote in the first post should work, but it's not showing me anything. Is there a problem with that code?

From looking at the pause scripts in ZS:DX and others, it seems like this should work:
Code: Lua
  1. function game:on_paused()
  2. local pause_infra_img = sol.surface.create("hud/pause_infra.png")
  3. end
  4.  

But that doesn't show anything either. I can't get either of those to work, am I missing something?

Diarandor

  • Hero Member
  • *****
  • Posts: 1008
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Displaying an image on pause
« Reply #3 on: February 27, 2018, 08:30:26 pm »
The command "pause_infra_img:draw(pause_surface)" draws that image only once (for 1 millisecond of maybe less).
To show the image, you need to draw it at each iteration, and for that, you need to call it inside some "on_draw" event.
Check scripts of finished projects to learn where that drawing code should be.
“If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you.”

Max

  • Full Member
  • ***
  • Posts: 155
    • View Profile
Re: Displaying an image on pause
« Reply #4 on: March 01, 2018, 12:02:20 am »
Awesome! Thanks, Diarandor! I should probably get used to typing that, haha.




So for anyone interested in replicating what I've done (as it's like a baby step toward building your first pause menu, haha), here's how it goes. I'll try to do a little tutorial my past self would appreciate. My goal was, when you pause the game, a box comes up with the current step of the main quest. It's a little more complicated for me because there's a number of times when there's two main quest lines that you can do in either order, but anyway. Here's how it looks.

In my game_manager script, I've defined what to do when the game is paused. "pause_infra" is a separate script I've created that will work as a menu, which is a special way the engine can treat any table, from what I understand. So when the game is paused, I get that script using the require() syntax. Then, now that I have it, I'm going to call one of its functions, as well as start the menu via sol.menu.start()

After the game is unpaused, I want to stop the menu so it doesn't stay up. I've also got the choice to save, continue, or quit the game, which I learned from Christopho's wonderful tutorial on his youtube channel.

Code: Lua
  1. --Pause Menu
  2. function game:on_paused()
  3.  
  4.   require("scripts/menus/pause_infra")
  5.   pause_infra:get_game(game)
  6.   sol.menu.start(game, pause_infra)
  7.  
  8.  
  9.   --save dialog
  10.   game:start_dialog("_game.pause", function(answer)
  11.     if answer == 1 then
  12.       game:set_paused(false)
  13.     elseif answer == 2 then
  14.       game:save()
  15.       game:set_paused(false)
  16.     elseif answer == 3 then
  17.       sol.main.exit()
  18.     end
  19.   end)
  20.  
  21.  
  22. end --end of on:paused function
  23.  
  24. function game:on_unpaused()
  25.   require("scripts/menus/pause_infra")
  26.   sol.menu.stop(pause_infra)
  27. end
  28.  


So that's all in the game_manager script. And here's the script I have that's called "pause_infra.lua"
I've added some comments to help anyone who might be able to learn from me.

Code: Lua
  1. pause_infra = {}    --this line means that we can refer to this a table called "pause_infra"
  2. local current_log_A       -- these two lines are just setting up some local variables I'm going to be using
  3. local current_log_B
  4.  
  5.  
  6.  
  7. --so if you remember in my game_manager script, I called pause_infra:get_game(game). I'm not 100% sure if I needed to type
  8. --"game" into the argument on both ends, maybe someone can correct me if I'm wrong. But the purpose of that is to give this
  9. --script the "game" userdata, just so it can call game:get_value(). This will allow us to display savegame values when we pause
  10. --the game, such as what our stats are, or in this case, what step of the main quest we're on.
  11.  
  12. function pause_infra:get_game(game)
  13.  
  14. --so what's going to happen here is I'm going to get the current step of the main quest from game:get_value("quest_log_a")
  15. --this value will be changed on each map where you complete a step. So for example, if you needed to defeat a monster,
  16. --when that happens, I'd change the value of "quest_log_a" in the monster's on_dead() event.
  17. --Then, I'm making a string variable called "current_log_A" which uses the number from "quest_log_a" to form the name of
  18. --a png file I have saved. So if you're on step 4 of the quest, current_log_A would equal "hud/quest_logs/quest_logA4.png"
  19. --Then this is repeated because I have two quest logs, an A and a B.
  20.  
  21.     local templog_a = game:get_value("quest_log_a")
  22.     current_log_A = "hud/quest_logs/quest_log_A"..templog_a..".png"
  23.  
  24.     local templog_b = game:get_value("quest_log_b")
  25.     current_log_B = "hud/quest_logs/quest_log_B"..templog_b..".png"
  26.  
  27. end
  28.  
  29.  
  30.  
  31. --Then this is where we actually display everything. The function pause_infra:on_draw() is automatically called by the engine
  32. --when the pause_infra menu is started (which I did in my game_manager script). You have to display images in the on_draw function
  33. --because otherwise, they'll only be drawn for 1 ms, which nobody will see. So now, every time the engine calles the :on_draw()
  34. --function (which seems to be constantly when the menu is running), the image will be displayed.
  35. --The way we do the image is to create a surface from a .PNG file, then just draw it.
  36. --I'm personally doing this for 3 things, 2 quest logs and a box around them. Later, I'll add in things like your attack or defense
  37. --stats, or an inventory, but this is just step one.
  38.  
  39. function pause_infra:on_draw(dst_surface)
  40.   --draw menu architecture
  41.   local pause_img = sol.surface.create("hud/pause_infra.png")
  42.   pause_img:draw(dst_surface)
  43.  
  44.   --draw quest log A
  45.   local log_a_img = sol.surface.create(current_log_A)
  46.   log_a_img:draw(dst_surface)
  47.  
  48.   --draw quest log B
  49.   local log_b_img = sol.surface.create(current_log_B)
  50.   log_b_img:draw(dst_surface)
  51.  
  52. end
  53.  




So yeah, thanks again, learning this has been super helpful, hopefully me documenting what I learned at a very fine level can help someone else trying to make a menu. I've still got a long way to go before I can make an item select menu, but this has been a good start!

llamazing

  • Full Member
  • ***
  • Posts: 118
    • View Profile
Re: Displaying an image on pause
« Reply #5 on: March 01, 2018, 04:17:35 am »
I'm assuming "hud/quest_logs/quest_log_A"..templog_a..".png" is an image of a text string. You'll have more flexibility if you use text_surfaces instead, and it will make localization easier. What you can do instead is create entries in strings.dat to define all of your quest log strings. Here is an example of how I would do it.

Code: Lua
  1. --strings.dat
  2. text{ key = "objective.save_princess", value = "Save the princess in the Mountain Temple" }
  3. text{ key = "objective.defeat_water_boss", value = "Defeat the boss of the Water Temple" }
  4.  
  5. ---------------
  6.  
  7. pause_infra = {}
  8. local current_log_A = sol.surface.create(400,50) --choose an appropriate size for the image
  9. local current_log_B = sol.surface.create(400,50) --ditto choose size
  10.  
  11. local text_surface = sol.text_surface.create({
  12.         --change these values as you see fit
  13.         font = "font_name",
  14.         rendering_mode = "solid", --or "antialiasing", depends on font (use solid for bitmap font)
  15.         color = {255, 255, 255}, --white, omit if using bitmap font
  16.         font_size = 12, --omit if using bitmap font
  17. })
  18. --note that a size is not specified for a text surface, it will be as big as the text it contains
  19.  
  20. function pause_infra:get_game(game) --if it were me I would name this pause_infra:update(game), and likewise call it any time the contents to be displayed have changed
  21.     local log_a_text_key = game:get_value("quest_log_a") --this string should match a key in strings.dat
  22.     text_surface:set_text_key(log_a_text_key) --gets localized string in current language
  23.     current_log_A:clear() --erase any previous content on surface
  24.     text_surface:draw(current_log_A) --puts rendered text on surface, may need to handle cases where clipping occurs
  25.  
  26.     local log_b_text_key = game:get_value("quest_log_b")
  27.     text_surface:set_text_key(log_b_text_key)
  28.     current_log_B:clear()
  29.     text_surface:draw(current_log_B)
  30.    
  31.     --Now current_log_A and current_log_B contain images rendered from text string, content won't change while pause menu open
  32. end
  33.  
  34.  
  35. local pause_img = sol.surface.create("hud/pause_infra.png") --move this function outside on_draw so you aren't destroying and re-creating this image a zillion times per second. You can do this because the image never changes
  36.  
  37. function pause_infra:on_draw(dst_surface)
  38.   --draw menu architecture
  39.   pause_img:draw(dst_surface)
  40.  
  41.   --draw quest log A
  42.   --delete this line: local log_a_img = sol.surface.create(current_log_A) --the contents of this surface don't change while the pause menu is open, so only create this surface whenever the pause menu is opened
  43.   current_log_A:draw(dst_surface, 100, 250) --specify coordinates for where you want to draw it on the screen
  44.  
  45.   --draw quest log B
  46.   --delete this line: local log_b_img = sol.surface.create(current_log_B) --the contents of this surface don't change while the pause menu is open, so only create this surface whenever the pause menu is opened
  47.   current_log_B:draw(dst_surface, 100, 300) --places right below log_A
  48. end

On another note, you want to do as little stuff as possible in in the on_draw() function as possible since it gets updated so frequently (see my comments above). In your case, the contents of your pause menu won't change (unless you decide to make it interactive). So what you want to do instead is determine what content to display whenever the pause menu is opened, and create intermediate surfaces containing the corresponding images. No need to regenerate them on every call of on_draw().

Max

  • Full Member
  • ***
  • Posts: 155
    • View Profile
Re: Displaying an image on pause
« Reply #6 on: March 02, 2018, 01:46:07 am »
Thanks llamazing! Yeah, the quest logs are images of text strings. I've gone and implemented your suggestions into my script, but it's given me a couple problems- the strings are cut off because they're too long and also I can only get the bottom half of the text to show. Here's what I did:

Code: Lua
  1. pause_infra = {}
  2. local current_log_A = sol.surface.create() --choose an appropriate size for the image
  3. local current_log_B = sol.surface.create() --ditto choose size
  4.  
  5. local text_surface = sol.text_surface.create({
  6.         font = "oceansfont",
  7. })
  8. --note that a size is not specified for a text surface, it will be as big as the text it contains
  9.  
  10. function pause_infra:update_game(game)
  11.  
  12.     local log_a_text_key = game:get_value("quest_log_a") --this string should match a key in strings.dat
  13.     current_log_A:clear() --erase any previous content on surface
  14.     text_surface:set_text_key(log_a_text_key) --gets localized string in current language
  15.     text_surface:draw(current_log_A) --puts rendered text on surface, may need to handle cases where clipping occurs
  16.  
  17.     local log_b_text_key = game:get_value("quest_log_b")
  18.     current_log_B:clear()
  19.     text_surface:set_text_key(log_b_text_key)
  20.     text_surface:draw(current_log_B)
  21.    
  22.     --Now current_log_A and current_log_B contain images rendered from text string, content won't change while pause menu open
  23. end
  24.  
  25.  
  26. local pause_img = sol.surface.create("hud/pause_infra.png")
  27.  
  28. function pause_infra:on_draw(dst_surface)
  29.   --draw menu architecture
  30.   pause_img:draw(dst_surface)
  31.  
  32.   --draw quest log A
  33.  
  34.   current_log_A:draw(dst_surface, 165, 27) --specify coordinates for where you want to draw it on the screen
  35.  
  36.   --draw quest log B
  37.   current_log_B:draw(dst_surface, 165, 62) --places right below log_A
  38.  
  39. end
  40.  

Any advice?

llamazing

  • Full Member
  • ***
  • Posts: 118
    • View Profile
Re: Displaying an image on pause
« Reply #7 on: March 02, 2018, 02:12:33 am »
Only the bottom of the text is showing because the default vertical alignment is middle. setting the vertical_alignment = "top" somewhere between lines 11-16 of the code I posted will fix that problem. The alternate way to handle this problem is to specify a y offset at lines 24 & 29 of my code. You are drawing the text_surface at coordinates (0,0) of the intermediate surface. If the intermediate surface has a height of 32 and your font is the same, then you could do text_surface:draw(current_log_A, 0 ,16), for example. (0,16) is where you want the middle left point of the text to go using top and middle alignment. For left and top alignment you want the left top point to be at (0,0).

The second problem with the text being cut off is trickier, and I didn't consider the possibility of you wanting the objectives to wrap across multiple lines of text. Your options are the following:
1) Fix where you want line breaks by adding "\n" in your string.dat text values. You will then have to split apart the text at the line breaks writing one line of text to the text_surface and intermediate surfaces at a time (and offset the lines appropriately when you specify the y coordinate to draw it at)
2) You could auto-wrap the text by splitting the text to a new line once it exceeds your max width (this would be done in pause_infra:get_game()). Like before you'd have to write each line to the text_surface separately. Keep in mind with this second method that the way you determine the text width varies depending on whether you are using a bitmap or non-bitmap font.

I'm willing to help you with this if you provide your pause_infra.png image, the font you are using for the objectives, and a few representative text strings from strings.dat. It's hard to give an example without knowing some of the specifics of your implementation.

EDIT: please also specify your quest size and give the x,y coordinates of where you are drawing pause_infra.png
« Last Edit: March 02, 2018, 02:16:10 am by llamazing »

Max

  • Full Member
  • ***
  • Posts: 155
    • View Profile
Re: Displaying an image on pause
« Reply #8 on: March 02, 2018, 02:25:15 am »
If you're willing to help, definitely! I've attached them to this post. The first one is the bitmap font I made for this, each letter is 7 pixels wide, just like the ALTTP font for convenience. I'm working in (what I think is default) 320x240 quest size. I made the pause_infra.png image the same size so that I could just draw it at 0,0. It's really just the box outlines.

Here's a few strings from the strings.dat:

text{ key = "0", value = "Find the chart salesman \n in Ballast Harbor" }   (I gave the line break thing a try to see what would happen)
text{ key = "a1", value = "Get some whisky from the secret stash" }
text{ key = "a2", value = "Meet Juglan at the pier" }
text{ key = "a3", value = "Investigat the Sodden Cormorant Tavern" }
text{ key = "a4", value = "Explore Spruce Head" }
text{ key = "a5", value = "Investigate Briarwood Distillery" }
text{ key = "a6", value = "Find a keyhole on Kingsdown Isle" }
text{ key = "a7", value = "Find clues in Hourglass Fort" }
text{ key = "b1", value = "Find the chart salesman in Ballast Harbor" }
text{ key = "b2", value = "Return the stolen charts" }
text{ key = "b3", value = "Find a keyhole for Mallow's Key" }


So if I'm understanding what I need to do to fix this, I'll want something like current_log_A1 and current_log_A2 surfaces?


The alignment thing was a quick fix, so that's nice. I'll have to think about it a little more to make sure I thoroughly understand it.

llamazing

  • Full Member
  • ***
  • Posts: 118
    • View Profile
Re: Displaying an image on pause
« Reply #9 on: March 02, 2018, 02:58:06 am »
Okay, great! Why don't you see if you can figure out how to do it, and I'll post how I would do it, then you can compare.

Here are some hints:
No need for additional surfaces. In fact, you can do it with just one text_surface and one surface (i.e. current_log). Size current_log to be something like 134x72 judging by the pause_infra.png image.

Then specify a different y offset for each line of text when you call text_surface:draw(current_log) (first line at 0,0; second line at 0,16; etc. Adjust appropriately for more white-space between lines).

For determining where the line breaks occur with a bitmap font (assuming you don't do the \n method), you can simply count 19 characters (133 pixels) before starting the next line.

Max

  • Full Member
  • ***
  • Posts: 155
    • View Profile
Re: Displaying an image on pause
« Reply #10 on: March 02, 2018, 04:01:50 am »
This is great, thanks! I've got a step in the right direction, I think. So far, my code is this:

Code: Lua
  1. function pause_infra:update_game(game)
  2.  
  3.     local log_a_text_key = game:get_value("quest_log_a") --this string should match a key in strings.dat
  4.     current_log_A:clear() --erase any previous content on surface
  5.     text_surface:set_text_key(log_a_text_key) --gets localized string in current language
  6.     text_surface:draw(current_log_A) --puts rendered text on surface, may need to handle cases where clipping occurs
  7.     text_surface:set_text("second line, somehow")
  8.     text_surface:draw(current_log_A, 0, 16)
  9.  
  10.     local log_b_text_key = game:get_value("quest_log_b")
  11.     current_log_B:clear()
  12.     text_surface:set_text_key(log_b_text_key)
  13.     text_surface:draw(current_log_B)
  14.     text_surface:set_text("second line, somehow")
  15.     text_surface:draw(current_log_B, 0, 16)
  16.     --Now current_log_A and current_log_B contain images rendered from text string, content won't change while pause menu open
  17.  
  18. end
  19.  

Once I find a way to get the part of my string after the line break, I'm pretty sure this will work. I've looked through the Solarus documentation quite a bit and I can't find any functions or anything to extract a line from a string (I've been working on this since you posted, haha). I'm currently looking through various Lua documentation and tutorials to figure this part out, any more hints as to where I'll find this sort of function?

Thanks for the help again, llamazing! I'm learning quite a bit!

llamazing

  • Full Member
  • ***
  • Posts: 118
    • View Profile
Re: Displaying an image on pause
« Reply #11 on: March 02, 2018, 04:38:55 am »
Hint2: Use the function sol.language.get_string(log_a_text_key) to get a localized string that you can play with instead of setting it directly using text_surface:set_text_key(log_a_text_key). Once you've split the string to the substring you want to use, set the text of the text_surface with text_surface:set_text(my_substring)

For help with splitting a string, see http://lua-users.org/wiki/StringLibraryTutorial
string.find() and string.sub() should be useful.
« Last Edit: March 02, 2018, 04:41:38 am by llamazing »

llamazing

  • Full Member
  • ***
  • Posts: 118
    • View Profile
Re: Displaying an image on pause
« Reply #12 on: March 02, 2018, 06:36:41 am »
Here is how I would do the script:
Code: Lua
  1. pause_infra = {}
  2. local current_log = sol.surface.create(138,72) --22 characters across, 4+ lines tall
  3.  
  4. local text_surface = sol.text_surface.create({
  5.     vertical_alignment = "top",
  6.     horizontal_alignment = "center",
  7.     font = "oceansfont",
  8. })
  9. --note that a size is not specified for a text surface, it will be as big as the text it contains
  10.  
  11. --Returns iterator to split text at line breaks
  12. local function line_it(text)
  13.     assert(type(text)=="string", "Bad argument #1 to 'line_it' (string expected, got "..type(text)..")")
  14.  
  15.     text = text:gsub("\r\n", "\n"):gsub("\r", "\n").."\n" --convert instances of \r to \n and add line break to end
  16.     return text:gmatch("([^\n]*)\n") -- Each line including empty ones.
  17. end
  18.  
  19.  
  20. function pause_infra:update_game(game)
  21.     current_log:clear() --erase any previous content on surface
  22.  
  23.     local log_a_text = game:get_value("quest_log_a") --the string saved to the game value "quest_log_a" should match a key in strings.dat
  24.     local next_line = line_it(sol.language.get_string(log_a_text)) --gets localized string in current language
  25.  
  26.     text_surface:set_text(next_line()) --line 1 of quest_log_a
  27.     text_surface:draw(current_log, 69, 0) --renders first line of quest_log_a text and draws on current_log surface
  28.         --x value 69 because horizontal alignment is center (138/2 = 69)
  29.  
  30.     text_surface:set_text(next_line()) --line 2 of quest_log_a
  31.     text_surface:draw(current_log, 69, 16)
  32.  
  33.     --NOTE: if quest_log_a contains more than 2 lines of text then the extra lines do not get drawn
  34.  
  35.     local log_b_text = game:get_value("quest_log_b")
  36.     next_line = line_it(sol.language.get_string(log_b_text))
  37.  
  38.     text_surface:set_text(next_line()) --line 1 of quest_log_b
  39.     text_surface:draw(current_log, 69, 39)
  40.  
  41.     text_surface:set_text(next_line()) --line 2 of quest_log_b
  42.     text_surface:draw(current_log, 69, 55)
  43.  
  44.     --Now current_log contains and image rendered from text strings of log A & log B (up to 4 lines)
  45.     --content won't change while pause menu open
  46. end
  47.  
  48.  
  49. local pause_img = sol.surface.create("hud/pause_infra.png")
  50.  
  51. function pause_infra:on_draw(dst_surface)
  52.     --draw menu architecture
  53.     pause_img:draw(dst_surface)
  54.  
  55.     --draw quest log
  56.     current_log:draw(dst_surface, 167, 27) --specify coordinates for where you want to draw it on the screen
  57. end

I used center horizontal alignment because it looked better. I had to adjust the x-coordinates to compensate, see my comments.

I went the route of manual line breaks using \n. The disadvantage is you have to ensure none of your lines of text exceed 22 characters or it will get clipped. But it also gives you more control of choosing where the line break occurs. With the other method you might get a first line that is really long and a second line that is only one short word.

Here are your string.dat strings with the \n breaks added:
text{ key = "0", value = "Find the chart salesman\nin Ballast Harbor" }
text{ key = "a1", value = "Get some whisky from\nthe secret stash" }
text{ key = "a2", value = "Meet Juglan\nat the pier" }
text{ key = "a3", value = "Investigate the Sodden\nCormorant Tavern" }
text{ key = "a4", value = "Explore Spruce Head" }
text{ key = "a5", value = "Investigate Briarwood\nDistillery" }
text{ key = "a6", value = "Find a keyhole\non Kingsdown Isle" }
text{ key = "a7", value = "Find clues in\nHourglass Fort" }
text{ key = "b1", value = "Find the chart salesman\nin Ballast Harbor" }
text{ key = "b2", value = "Return the\nstolen charts" }
text{ key = "b3", value = "Find a keyhole\nfor Mallow's Key" }

Also note that you can use the following notation in string.dat if you prefer. It might make it easier to tell if you've exceeded 22 characters on a given line. With this style, the line break is an actual line break rather than \n.
Code: Lua
  1. text{ key = "0", value = [[
  2. Find the chart salesman
  3. in Ballast Harbor
  4. ]]}

For splitting the strings into lines I borrowed the line iterator from the ATTP dialog box script. Look up iterators if you are having trouble following how it works.
« Last Edit: March 02, 2018, 06:39:52 am by llamazing »

Max

  • Full Member
  • ***
  • Posts: 155
    • View Profile
Re: Displaying an image on pause
« Reply #13 on: March 02, 2018, 09:31:01 pm »
That works great! I switched the horizontal alignment back to "left" both as practice for learning how alignment affects the rest of the script and because it feels more like it's written in a journal when it's aligned left. But yeah, totally just personal preference. Here's what I did, which is basically what you have with tweaked values, and it works wonderfully.

Code: Lua
  1. pause_infra = {}
  2. local current_log = sol.surface.create(144, 72)
  3.  
  4. local text_surface = sol.text_surface.create({
  5.         font = "oceansfont",
  6.         vertical_alignment = "top",
  7.         horizontal_alignment = "left",
  8. })
  9. --note that a size is not specified for a text surface, it will be as big as the text it contains
  10.  
  11.  
  12. --Returns iterator to split text at line breaks
  13. local function line_it(text)
  14.     assert(type(text)=="string", "Bad argument #1 to 'line_it' (string expected, got "..type(text)..")")
  15.  
  16.     text = text:gsub("\r\n", "\n"):gsub("\r", "\n").."\n" --convert instances of \r to \n and add line break to end
  17.     return text:gmatch("([^\n]*)\n") -- Each line including empty ones.
  18. end
  19.  
  20.  
  21. function pause_infra:update_game(game)
  22.  
  23.     current_log:clear()
  24.  
  25.     local log_a_text = game:get_value("quest_log_a") --the string saved to the game value "quest_log_a" should match a key in strings.dat
  26.     local next_line = line_it(sol.language.get_string(log_a_text)) --gets localized string in current language
  27.  
  28.     text_surface:set_text(next_line()) --line 1 of quest_log_a
  29.     text_surface:draw(current_log, 0, 0) --renders first line of quest_log_a text and draws on current_log surface
  30.  
  31.     text_surface:set_text(next_line()) --line 2 of quest_log_a
  32.     text_surface:draw(current_log, 0, 16)
  33.  
  34.     --NOTE: if quest_log_a contains more than 2 lines of text then the extra lines do not get drawn
  35.  
  36.     local log_b_text = game:get_value("quest_log_b")
  37.     next_line = line_it(sol.language.get_string(log_b_text))
  38.  
  39.     text_surface:set_text(next_line()) --line 1 of quest_log_b
  40.     text_surface:draw(current_log, 0, 39)
  41.  
  42.     text_surface:set_text(next_line()) --line 2 of quest_log_b
  43.     text_surface:draw(current_log, 0, 55)
  44.  
  45.     --Now current_log contains and image rendered from text strings of log A & log B (up to 4 lines)
  46.     --content won't change while pause menu open
  47. end
  48.  
  49.  
  50. local pause_img = sol.surface.create("hud/pause_infra.png")
  51.  
  52. function pause_infra:on_draw(dst_surface)
  53.   --draw menu architecture
  54.   pause_img:draw(dst_surface)
  55.   --draw quest log A
  56.   current_log:draw(dst_surface, 166, 27) --specify coordinates for where you want to draw it on the screen
  57.  
  58. end
  59.  


Now, I don't ever plan on using more than two lines for the quest log items, but for someone who might, I think you'd do, for example, 4 lines, like this? (starting at line 30)

Code: Lua
  1. text_surface:set_text(next_line()) --line 1 of quest_log_a
  2.     text_surface:draw(current_log, 0, 0) --renders first line of quest_log_a text and draws on current_log surface
  3.  
  4.     text_surface:set_text(next_line()) --line 2 of quest_log_a
  5.     text_surface:draw(current_log, 0, 16)
  6.  
  7.     text_surface:set_text(next_line()) --line 3 of quest_log_a
  8.     text_surface:draw(current_log, 0, 32)
  9.  
  10.     text_surface:set_text(next_line()) --line 4 of quest_log_a
  11.     text_surface:draw(current_log, 0, 48)
  12.  



So far far as the line_it function, I think I've got a grasp of it. The tricky line is

return text:gmatch("([^\n]*)\n")

From what I'm researching, this returns a pattern finding iterator, which (I've still got some research on iterators to fully understand them) will basically return some separate strings based on the pattern supplied to gmatch, like gmatch(pattern). The pattern you're using is encased in quotes, because patterns need to be strings, I think? Then you've got ([^\n]*)\n.  The ^ symbol basically means, every character other than- in this case, since \n means line break, ^\n means everything other than line breaks. And this is in square brackets because the ^\n is a char-set you've created- but since you're char-set only contains one type (characters other than line breaks), is it necessary to make this its own char-set, or just a convention? Anyway, after that, you've got an asterisk, *, which is basically there to return empty lines, as it means no character will count as a character other than a line break. So all together, it basically means match and send back all characters other than line breaks that end with a line break, which since there's a line break added to the end of each line in the code above this, will send back individual lines.


This definitely requires a full scoop of lua knowledge, so I'm taking a lot out of this! Thanks so much, llamazing!

llamazing

  • Full Member
  • ***
  • Posts: 118
    • View Profile
Re: Displaying an image on pause
« Reply #14 on: March 03, 2018, 12:14:14 am »
Now, I don't ever plan on using more than two lines for the quest log items, but for someone who might, I think you'd do, for example, 4 lines, like this? (starting at line 30)
Correct

The pattern you're using is encased in quotes, because patterns need to be strings, I think?
Correct

but since you're char-set only contains one type (characters other than line breaks), is it necessary to make this its own char-set, or just a convention?
It is necessary to use a char-set because the ^ character has a different meaning when used outside of the [] brackets (forces the match to be from the beginning of the string when used as the first character of the pattern).

Anyway, after that, you've got an asterisk, *, which is basically there to return empty lines, as it means no character will count as a character other than a line break. So all together, it basically means match and send back all characters other than line breaks that end with a line break, which since there's a line break added to the end of each line in the code above this, will send back individual lines.
Correct. Note that the asterisk also serves the purpose of capturing more than one consecutive instance of the [^\n] char-set and will match as many as possible. If you didn't want to return empty lines then you could change the * to +.