About color text dialogs

Started by Diarandor, July 10, 2015, 04:29:00 PM

Previous topic - Next topic
April 17, 2017, 02:08:28 PM #15 Last Edit: April 17, 2017, 02:38:05 PM by MetalZelda
Also, another cool feature you could add to your dialogbox script is savegame value parsing.
You will find example bellow and add it if you want. But a such basic feature is very useful for a lot of possibilities.

Example: displaying values (items, time, command, etc ...)

This could be useful for some texts, displaying the current value of multiple variable for example (since $v is only 1 value)
Here, using this method, I'm displaying all item commands for using an item, keyboard and Joypad.
Good news is, you can even use color


It's plug & play for the most part
Vous obtenez l'$[red]Arc du Héros$[white]!$0
Dans le menu $[yellow]pause$[white], écran $[blue]Inventaire$[white],
assignez l'arc grâce aux touches $[red]$(item_1)$[white]
ou $[red]$(item_2)$[white].


And also, it's cheap stuff, you don't need to recreate a surface
Code (lua) Select
elseif current_char == "(" then
    -- Predefined text (an input for example).
    local next_char = line:sub(self.char_index, self.char_index)
    -- The syntax is "$(text)".
    local right_position = line:find (")", self.char_index, true)
    local text = line:sub(self.char_index, right_position - 1)
    self.char_index = right_position + 1

-- Is the text a valid savegame value ?
if game:get_value(text) ~= nil then
  text = game:get_value(text)
end

        -- Using $(item_1) or such (warning, do not use a savagame value), as it is in the table bellow, will display a pre-defined text_properties
        -- with as value keyboard / joypad binding
local actions = {"item_1", "item_2", "action", "attack", "pause", "up", "down", "left", "right"}
    for i = 1, #actions do
  if text == actions[i] then
    text = game:get_command_keyboard_binding(text) .. " / " .. game:get_command_joypad_binding(text)
  end
end
self:set_text(text)


You just need
Code (lua) Select
function dialog_box:set_text(text)
    local current_line = self.current_line_surface[self.line_index]
    current_line:set_text(current_line:get_text() .. text)
  end


Another example of a clever usage


$v is already used by "Skulltula d'Or"(the object caption, so when translating, the item name will be already translated), so the item dialog is.
$[red]- $v -$[white]
On dit que ces $[yellow]$v$[white] ont
la possibilité de détruire des malédictions.
Vous en avez tué $[red]$(amount_of_skulltulas)$[white] sur 100 !


The value of killed skulltulas is parsed from the code above

The next cool stuff to add is icons in texts

April 17, 2017, 04:44:21 PM #16 Last Edit: April 17, 2017, 04:46:13 PM by Diarandor
I agree with @llamazing and @MetalZelda that putting names to new text surfaces in the dialog_box is really unnecessary. The idea I had in mind when I coded that was to allow to go back to the previous text surface if needed, to reduce the number of surfaces, but since the dialog box is small we should not worry about this kind of things. Better to reduce the syntax as the ones you use.

A good idea, and probably a better syntax, would be allowing to call functions in dialogs. So I propose a slight modification of the same syntax, which is even shorter:
$function_identifier

The functions associated to each "function_identifier" would be defined in the dialog box as usual: "dialog_box:function_name()".

To avoid using long function names in the dialog box (the syntax would be too long) and at the same time keep the long name in the definition of the functions of the form "dialog_box:function_name()" (which is clearer), we could use a short identifier after the dollar symbol "$" instead of the name of the function. For this purpose, another function "dialog_box:start_function(short_id, parameters)" would be needed, just to start the convenient function assotiated to that "short id" used in the syntax.

Furthermore, to keep the dialog as clean as possible, the parameters of each function should better be given as properties of the dialog and not in the middle of the text. For instance, if the color is changed 3 times in a dialog, we would define an array of color properties, and each time $c is called, the next color of the array would be applied.

Remark: $c could be used to call a function "dialog_box:set_color(color)". This function should be overloaded to be compatible too with this other useful syntax: "dialog_box:set_color(r, g, b)", whose parameters would be sent as strings and then "dialog_box:set_color(r, g, b)" would first convert the three strings into integers and then make the color change.

An example to change color and a shaking effect:
dialog{
  id = "_cannot_lift_too_heavy",
  skip = "all",
  color = {"red", "white", {200, 0, 100}},
  movement = {{"horizontal", "fast"}, nil, {"vertical", "slow"}, nil},
  text = [[
Oh là là, c'est l'$camour$c !

$cWe will $mshake$m and $mbounce$m !
]]
}

I propose some short function ids (i.e., for the dialog syntax):

$0, $1, $2, $3, etc: as usual, for the "text speed"
$c: change color; allows color_id_string  or (r, g, b) as parameters
$s: change the size of the text; integer parameter
$f: change the font type; font_id_string parameter
$m: movements and shaking effects; parameters: movement_type_string, and other optional_parameters
$d: "dynamic" (i.e., continuous) color changes (rainbow color-shift, etc). parameters: effect_id_string, and others
$b: change the "beeping" sounds, which may be different for different NPCs, etc; parameter: sound_id_string
$i: draw image/icon (the width may be given as parameter if necessary). parameters: image_id_string, etc

If you have more ideas for other different effects that we may implement, or a suggestion for a different syntax, we can discuss it.

A different syntax, much clearer for translators and easier to read, but harder to code (because there could be a "nested" use), would be to use "{", "}" symbols (these could be optional) to indicate where the effects will be applied. For instance:
blablabla this $c{word} and $c{this} are in red.
Another example:
dialog{
  id = "_cannot_lift_too_heavy",
  skip = "all",
  color = {"red", {200, 0, 100}},
  movement = {{"horizontal", "fast"}, {"vertical", "slow"}},
  text = [[
Oh là là, c'est l'$c{amour} !

We will $c{$m{shake} and $m{bounce}} !
]]
}
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

That could be cool, but it could also be tricky, if we use a lot of texts effects (using different colors in the same line for example), but I agree that text properties should be exploited.

I should also give these idea to exploit the text properties, it will make things better and easier, since I am, in parallel to y project, porting the Project Zelda Engine from RPG Maker to Solarus

  -> Dialog Box style (aka graphics), allow to define at the start of the text the dialog box graphics, automatically reset to "default" at the end of the dialog (https://www.youtube.com/watch?v=6u20RV_GwzE)
  -> Dialog Box positionning, sometimes, the dialogbox can clip automatically depending on the hero position or other stuffs, this parameter will set the position of this dialog, no matter the hero position or such
  -> Bank system (Idea from Zefk, http://forum.solarus-games.org/index.php/topic,806.0.html), where the system will be triggered by a text property and integrated entirely in the Dialog box script.

What do you think would be better of these options for the dialog box script? (I mean, for a new script that I will make.)
Non-nested properties (as the ones you use)? Or nested ones (like the ones of the end of my last comment)?
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

I think both of our idea will be useful for other, dialog style and positionning is kinda easy to do

Code (lua) Select
if dialog.position then
  local pos = dialog.position 
  if pos == "top" then
    y = 24
      elseif pos == "middle" then
        y = 88
      else
        y = 144
  end
  self.box_dst_position.y = y
end


Of course, this example assume that you are constantly using a 240 height resolution

Code (lua) Select
if dialog.style then
  game:set_dialog_style(dialog.style)
end


game:set_dialog_style (if not defined) just change and reload a surface.

MetalZelda, I added the dialog_style options to my dialog box - I actually had forgotten this wasn't standard.
Code (lua) Select
function game:set_dialog_style(style)
  dialog_box.style = style
  if style == "wood" then
    dialog_box.box_img = sol.surface.create("hud/dialog_box_wood.png")
    dialog_box.end_lines_sprite:set_animation("wood")
  elseif style == "stone" then
    dialog_box.box_img = sol.surface.create("hud/dialog_box_stone.png")
    dialog_box.end_lines_sprite:set_animation("stone")
  else
    dialog_box.box_img = sol.surface.create("hud/dialog_box.png")
    dialog_box.end_lines_sprite:set_animation("default")
  end
end


I also recently added the ability to pass, for example, an NPC name to the dialog box so it would display above it. This might be a useful feature. I also know someone (maybe it was Diarandor?) added NPC images to the dialog box, which could be a good standard feature.

Quote from: wrightmat on April 17, 2017, 09:08:14 PM
MetalZelda, I added the dialog_style options to my dialog box - I actually had forgotten this wasn't standard.
Code (lua) Select
function game:set_dialog_style(style)
  dialog_box.style = style
  if style == "wood" then
    dialog_box.box_img = sol.surface.create("hud/dialog_box_wood.png")
    dialog_box.end_lines_sprite:set_animation("wood")
  elseif style == "stone" then
    dialog_box.box_img = sol.surface.create("hud/dialog_box_stone.png")
    dialog_box.end_lines_sprite:set_animation("stone")
  else
    dialog_box.box_img = sol.surface.create("hud/dialog_box.png")
    dialog_box.end_lines_sprite:set_animation("default")
  end
end


I also recently added the ability to pass, for example, an NPC name to the dialog box so it would display above it. This might be a useful feature. I also know someone (maybe it was Diarandor?) added NPC images to the dialog box, which could be a good standard feature.

You are right, yet, I explained that set_dialog_style only recreate a new surface with a different graphic  :P

@wrightmat: Yes, I modified Christopho's script to allow displaying a sprite and choosing its animation.
Your idea that allows passing the name of the NPC is really good too; it is a nice feature to add to the scripts.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

Quote from: MetalZelda on April 15, 2017, 02:06:03 PM
I also suggest adding a horizontal text alignment thing like $vertical{"center"}

An example more in-line with what I posted earlier would be something more like the following, using $a for horizontal alignment:
$a{center}centered text$a0

Although horizontal alignment is a tricky one because it doesn't really make sense to have more than one alignment style per line. Maybe alignment settings should be ignored unless specified at the beginning of a line/paragraph?

Also, horizontal alignment is very doable, but I'm not sure how vertical alignment would work, since isn't each line sized vertically to fit the text anyway? Maybe if you were using multiple font sizes on a single line? Or perhaps you mean to vertically align an entire paragraph of text relative to the text box?




Quote from: Diarandor on April 15, 2017, 05:22:30 PM
It would be very useful too to add a feature that manages the change of line of the dialog box if the max lenght of one line is given. I don't have time these days to work on this, though.

My Cythera dialog script does this.




Quote from: MetalZelda on April 16, 2017, 01:54:20 AM
There is still the spacing problem if you use a special characters such as é, è, ç, à and Asian type characters

My Cythera dialog script seemed to work just fine with the European accents. I didn't try Asian characters, but that would only be a problem if the characters exceeded the bounds specified by text_surface:get_size().




Quote from: MetalZelda on April 17, 2017, 02:08:28 PM
Also, another cool feature you could add to your dialogbox script is savegame value parsing.

In keeping with the other notation, I'd suggest $g for savegame variables, e.g.:
assignez l'arc grâce aux touches $c{red}$g{item_1}$c{white}
ou $c{red}$g{item_2}$c{white}.


Also, there's no reason the dialog has to be limited to only one instance of $v. I allowed multiple in my Cythera dialog script.




Quote from: Diarandor on April 17, 2017, 07:05:49 PM
What do you think would be better of these options for the dialog box script? (I mean, for a new script that I will make.)
Non-nested properties (as the ones you use)? Or nested ones (like the ones of the end of my last comment)?

I can't say I'm a fan of the nested notation. It would be burdensome with lots of different values in a single dialog and potentially confusing to translators.

Another advantage to using the non-nested notation is that it works better with dynamic text. For example, say the player is selling their items at an item shop. A dialog may be something like "You sold the Hero Shield for 15g", which could be coded like:
"You sold the $v for $c{yellow}$vg$c0"

Then you could have strings.dat entries defining item names like so:
text{ key="item_hero_shield", value="$c{green}Hero Shield$c0"}
text{ key="item_ice_rod", "value="$c{blue}Ice Rod$c0"}


In this example, different items have different colors, so the color to use is dynamic and depends on which item the player sells. Also note that the color gets defined by the strings.dat entry rather than the dialogs.dat text.

As for the functions calls idea, it could work with the shorthand notation, but the parsing full function names becomes much trickier and is error prone if the function name is typo'ed, for example.

Instead of rechoosing whiteafter changing color, you could choose default. So that, if the default color of the text is black (for example) it will become black again. For portabilty, it may be easier.