Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

color crazy strings #16

Open
SquidPony opened this issue Apr 25, 2012 · 22 comments
Open

color crazy strings #16

SquidPony opened this issue Apr 25, 2012 · 22 comments
Assignees

Comments

@SquidPony
Copy link
Collaborator

add the ability to pass in an array of colors for the foreground and background to colorize when a string prints

@ghost ghost assigned SquidPony Apr 25, 2012
@smelc
Copy link
Contributor

smelc commented Nov 13, 2015

The method that takes an IColoredString in SquidPanel does half the job: it allows to give a foreground color per character of the string being printed.

@Rakaneth
Copy link
Contributor

Rakaneth commented Oct 20, 2017

I am trying to use a SquidMessageBox to display an IColoredString.Impl. Regular strings will display, but IColoredString.Impl does not.

	IColoredString<Color> text = new IColoredString.Impl<Color>("Test Text", SColor.GREEN);
	messages.appendWrappingMessage(text);
	messages.appendWrappingMessage("test message");

image

@smelc
Copy link
Contributor

smelc commented Oct 20, 2017

@Rakaneth > cool screenshot :-) @tommyettinger will be able to help you as he coded SquidMessageBox.

@Rakaneth
Copy link
Contributor

Rakaneth commented Oct 20, 2017

@smelc I did try replacing this with a TextPanel, which does properly display the color string; however, now I have a problem that new text added to the panel after initialization isn't getting drawn..

@tommyettinger
Copy link
Member

I'll take a look at the code and try writing or improving a demo to test this better than the current tests do. It seems like I missed something in the demos.

Also, Wolf's Den is a great name.

@smelc
Copy link
Contributor

smelc commented Oct 20, 2017

@Rakaneth > Indeed TextPanel is designed for displaying text that is known at initialization time. It'll never update its text afterwards. You should try LinesPanel if you wanna draw using libgdx's Bitmap font directly, it's the gdx-direct alternative to SquidMessageBox.

tommyettinger added a commit that referenced this issue Oct 20, 2017
This is relevant for #16 . The implementation of appendMessage() for
String correctly added an item to the internal List of IColoredString
values, but appendMessage() for IColoredString did not. This should work
now.

I was considering adding a method to IMarkup to escape non-markup usage
of characters that might be mistaken for markup, but it doesn't seem
needed and would break anything that implements IMarkup. So, the code I
wrote is only present in comments in markup-related files.
@tommyettinger
Copy link
Member

The first issue mentioned should be fixed as of the latest commit; some of the SquidMessageBox code updated the List of IColoredStrings when a String was appended, but didn't update that List when an IColoredString was added. SquidMessageBox should work now. As smelc said, LinesPanel should fit your needs; it can also handle non-fixed-width fonts (several are in DefaultResources; I like DefaultResources.getStretchablePrintFont() for TextPanel and LinesPanel fonts). If you look at the version history of smelc's game Dungeon Mercenary, the support for variable-width fonts made the descriptions and messages much easier to read and better-looking in his game.

@Rakaneth
Copy link
Contributor

Rakaneth commented Oct 21, 2017

Thanks for the update! I did wire up a LinesPanel and it suits my needs for the moment. I will check out the SquidMessageBox fix as soon as I get some free time (currently at a LARP right now!) I like the auto-scrolling and box-character bordering of SquidMessageBox, something I miss in LinesPanel.

On a separate note, I did try to throw together another implementation of IMarkup to hand to LinesPanel, one that would take color names instead:

This string is [red]red.[]

I couldn't find a way to get a String name of a Color, though; is this possible?

@tommyettinger
Copy link
Member

It's possible for SColor objects (there's a huge amount of predefined SColor objects as constants in that class), but Color is in libGDX, and we can't directly change Color. SColor extends libGDX's Color class, only adding a name field. While it's trivial to get a String name from an SColor (you can just get its name field, which is read-only, or call getName() on it), Color objects from GDX don't store any kind of name inside themselves. Thankfully, as of the latest commit all SColors with assigned names should be registered in libGDX's Colors class, which is what GDX's color markup uses to look up colors by name. If you make an IMarkup<SColor>, then it can get the name of the color from the SColor with mySColor.name or mySColor.getName() as long as it has a reasonable name; any SColor that was created without a name uses Color's toString() result, which is 8 hexadecimal digits in RGBA order -- hardly what we want for a name! This has an odd effect in that your IMarkup<SColor> would know how to get the name from an SColor, and the markup in GDX would look it up in Colors to get a regular Color version of the same SColor. The markup would only do this lookup inside libGDX, so you're probably not affected by it using Color while you use SColor.

LARP on!

@Rakaneth
Copy link
Contributor

I had a moment to play with this.

Did the default dungeon palette get changed? (Related question: can I generate my own palettes for these things?)

image

@tommyettinger
Copy link
Member

Woah... it shouldn't have changed to that, if it changed at all since you last used it. DungeonUtility in the squidlib-util module has some older version of the palette setting I think, and the current version is in MapUtility in the squidlib module. The latest change only affected water colors and the animation for grass, water, and "lake" contents (if a map has them), 21 days ago, and walls shouldn't be so dark if the background is also fairly dark... You can maybe print the contents of SColor.LIMITED_PALETTE to see if they match what you expect. My output when I run System.out.println(StringKit.join(",\n", SColor.LIMITED_PALETTE)); looks like this:

Pure Dark Gray 3f4041ff,
Cream fffdd0ff,
Flattery Brown 6b4423ff,
Silver Grey 97867cff,
Rust b7410eff,
Pale Cornflower Blue abcdefff,
International Orange ff4f00ff,
White ffffffff,
Light Gray c0c0c0ff,
Dark Gray 404040ff,
Red Incense f07f5eff,
Red ff0000ff,
Cochineal Red 9d2933ff,
Peach Orange ffcc99ff,
Orange Peel ffa000ff,
Tangerine f28500ff,
Lemon Chiffon fffacdff,
Corn fbec5dff,
Golden Yellow ffdf00ff,
Mint Green 98ff98ff,
Lime Green 32cd32ff,
Green Bamboo 006442ff,
Cyan 00ffffff,
CW Dark Azure 1b819aff,
Midori 2a606bff,
Columbia Blue 9bddffff,
Royal Blue 4169e1ff,
Persian Blue 1c39bbff,
Lavender Blue ccccffff,
Dark Violet 9400d3ff,
Indigo 4b0082ff,
Carnation Pink ffa6c9ff,
Hot Magenta ff00ccff,
Light Maroon b03060ff,
Tan d2b48cff,
Dark Tan 918151ff,
Pale Brown 987654ff,
Steamed Chestnut d3b17dff,
Dark Chestnut 986960ff,
Sappanwood Incense a24f46ff

If the third item in the list doesn't look like Flattery Brown 6b4423ff, then something changed in your program's palette. But, you can just set the colors in SColor.LIMITED_PALETTE to whatever SColor values you want (you can't change the array's size or set the whole array at once, but you can set individual items like SColor.LIMITED_PALETTE[2] = SColor.FLATTERY_BROWN), and changes to that palette will be used by the default map coloring methods. It's also possible you were using the methods in SquidPanel or SquidLayers that took an int to specify a color from a palette; I think those were removed a while ago because they made both classes huge (there were a few dozen methods like that) and didn't provide new functionality or shorter code (you could just pass myPalette.get(myIndex) to give a Color, instead of passing myIndex, myPalette in an indirect way). It might also be that the brightness or background color isn't correct for some reason, either in SquidLib or it was changed in your code somewhere. If your code is open source I'd be happy to look through it for possible reasons why this would happen.

@Rakaneth
Copy link
Contributor

Rakaneth commented Oct 23, 2017

I got it figured out. I used SquidLayers.put(x, y, c, fgIndex, bgIndex, lightness) which I think was replaced with SquidLayers.put(x, y, c, encodedForeground, encodedBackground, lightness) in a recent version.

I fixed this by reaching directly into SColor.LIMITED_PALETTE to get the old colors, then using SquidLayers.put(x, y, c, fgColor, bgColor, lightness).

@Rakaneth
Copy link
Contributor

Rakaneth commented Oct 23, 2017

Now, I have a different issue. I've gone back to SquidMessageBox, but SquidMessageBox doesn't take markup. I'm trying to find a way to get SquidMessageBox to understand an IMarkup, I think I need to extend SquidMessageBox and add an IMarkup to the constructor and as a field, but I don't know how to link the two together.

@tommyettinger
Copy link
Member

tommyettinger commented Oct 23, 2017

What are you trying to do with the markup? Is this your custom implementation that uses names of colors? I'm not totally sure how all of the code involved fits together, but libGDX can be configured to treat a String like "Very [RED]HOT[] Spices" as the text "Very HOT Spices" with the middle word colored red, as long as that whole String is given as one item for BitmapFont to draw. A possible issue is that SquidMessageBox draws chars one at a time, even when given a long String, so the markup would essentially be ignored even if markup was enabled for that font. This is part of what LinesPanel is meant to solve, since it draws text using BitmapFont to draw the whole line. I think that it won't be too hard for me to change or add a method to draw a String with a specific IMarkup to SquidPanel (and thus to SquidMessageBox), but my computer is acting up currently... I'll see what I can do.

EDIT: You can enable markup on a BitmapFont with font.getData().markupEnabled = true; or if you have a TextCellFactory called textFactory (which it is in SquidPanel and SquidMessageBox), with textFactory.bmpFont.getData().markupEnabled = true;. Without this, the square brackets will be drawn as normal characters for certain. With markup enabled, it still might not work if rendering happens one char at a time, which I'm working on now.

@Rakaneth
Copy link
Contributor

Rakaneth commented Oct 23, 2017

Right now, I just want to be able to send IMarked-up strings to a SquidMessageBox. I prefer SquidMessageBox for displaying player messages because it scrolls and remembers messages. I also prefer its aesthetic. I'm currently cobbling together an intermediate solution that uses regexes to append to an IColoredString in the meantime.

tommyettinger added a commit that referenced this issue Oct 24, 2017
Most of the distance field fonts now include the full block image at
Unicode codepoint 0. That block was previously a separate Texture, and
that has performance downsides when mostly rendering text. By keeping an
entire text-based line as potentially one String with different colors,
we also open up possibilities for both optimizing our drawing a little
better, and for using markup on long Strings (as I'd like to have for
#16 )Though the fonts should keep the changes in this commit for later
(it gave me a chance to improve some unrelated spacing issues in some
fonts), TextCellFactory will have its changes in this commit immediately
reverted, since not all fonts have a block at codepoint 0, including
probably all user-created fonts. The solution is probably to use a
subclass of TextCellFactory for fonts we know have been made to use the
block in the font, and default to the normal separate texture for all
other fonts.
tommyettinger added a commit that referenced this issue Oct 25, 2017
This was directly inspired by #16 and Rakaneth's approach to generating
IColoredStrings by parsing GDX color markup; this way also uses regular
expressions, but uses RegExodus like the rest of SquidLib for GWT
compatibility. Some more-advanced and less-documented RegExodus features
are used here to simplify the code, at least for me since I've attempted
to understand RegExodus for a while now. This can generate colorful text
like at the bottom of this screenshot, https://i.imgur.com/CBsLzdL.png ,
using code like this:
```
IColoredString<Color> text = GDXMarkup.instance.colorString(
"Use numpad or vi-keys ([CW Bright Red]h[CW Bright Apricot]j[CW Bright
Yellow]k[CW Bright Lime]l" +
"[CW Bright Jade]y[CW Bright Azure]u[CW Bright Sapphire]b[CW Flush
Purple]n[]) to move. Use " +
"[CW Pale Indigo]?[] for help, [CW Faded Brown]f[] to filter colors, [CW
Gray White]q[] to quit. " +
"Click the top or bottom border of this box to scroll.");
```
This wrapper around GDX markup isn't the most convenient way of
specifying colors that I can think of, but it's pretty nice.
@tommyettinger
Copy link
Member

@Rakaneth , I liked your idea to parse markup in a straightforward way with regexes, so I implemented it in the last commit, 467ff8c . It uses the RegExodus library (which SquidLib already depends on) instead of java.util.regex, for GWT compatibility. Currently you can generate colorful text
like at the bottom of this screenshot, https://i.imgur.com/CBsLzdL.png ,
using code like this:

IColoredString<Color> text = GDXMarkup.instance.colorString(
"Use numpad or vi-keys ([CW Bright Red]h[CW Bright Apricot]j[CW Bright
Yellow]k[CW Bright Lime]l" +
"[CW Bright Jade]y[CW Bright Azure]u[CW Bright Sapphire]b[CW Flush
Purple]n[]) to move. Use " +
"[CW Pale Indigo]?[] for help, [CW Faded Brown]f[] to filter colors, [CW
Gray White]q[] to quit. " +
"Click the top or bottom border of this box to scroll.");

If you have ideas for extra features, we aren't limited to the things GDX's color markup supports, and SquidLib could support extra syntax and options that build on GDX markup.

@Rakaneth
Copy link
Contributor

Glorious colored SquidMessageBox strings!

image

@tommyettinger
Copy link
Member

That looks great! I can see advantages for lots of games using this, like NetHack (with many colors of dragon) could differentiate which enemy did which action by coloring the name of the enemy to match their color in the game map. That could be like "The [Red]Dragon[] breathes fire at you!" Somewhat off-topic, but what do "Dmg 2k2" and "Atk 2k1" mean in your screenshot?

@Rakaneth
Copy link
Contributor

Rakaneth commented Oct 26, 2017

The "XkY" notation means to roll X dice and keep the highest Y (something I got from the 7th Sea tabletop RPG). In this case, the dice are exploding d6s.

An attacker rolls his Atk value, trying to meet or exceed his opponent's Def value. If so, he rolls his Dmg value to determine how much raw damage is dealt; this is increased by a high Attack roll and reduced by armor and magical protection(values not shown in screenshot, but will be plugged into the HUD soon).

The dice values are currently raw Strings that my custom Dice module knows how to roll, but I am considering making them a small class instead.

EDIT: I just saw the SquidLib Dice module. Dice.bestOf() works perfectly for this, and I've just updated my combat interface to use it instead. I've also made a class of these and the code is much easier to understand.

@tommyettinger
Copy link
Member

Ah, glad Dice is proving useful! I just started adding a few features to its dice notation parsing a few days ago, and since you mentioned it, today I went back and added support in the notation for roll(String) for best-of exploding dice, as well as ranges with random upper bounds (allowing, effectively, dice with a random number of sides). That's in commit dbc2c9d , the latest one. I'm also working on possible expansions to the markup system to allow changing the font style to bold or italic for some section of a screen, and use other styles for other parts.

@smelc
Copy link
Contributor

smelc commented Oct 27, 2017

@tommyettinger > I suppose bold and italic require providing additional bitmap fonts ?

@tommyettinger
Copy link
Member

@smelc , sort of. The way I have them implemented in TextFamily (a subclass of TextCellFactory that delegates most of its work to its parent), these multi-part fonts load normal, bold, italic, and bold italic fonts from an atlas (which can hold more textures, if needed). A small addition to TextCellFactory, the setStyle() method, does nothing when called in TextCellFactory but changes the style in TextFamily. Lastly, one of the draw() methods in TextCellFactory now uses the top two bits of a char to establish bold and italic as on or off, and keeps the rest of the char the same. This last part may change to only use those two bits in TextFamily, rather than reducing the Unicode range for all fonts in TextCellFactory as well. Two font families are provided in DefaultResources (Iosevka and Iosevka Slab, since I had all the ttf files available), which are distance field fonts. GDXMarkup can now take [*] and [/] to toggle bold and italic, respectively, and put this in the generated IColoredString using the top two bits of each char. This has a nice benefit of not requiring much change to existing code, but some stuff may still be rough currently.

tommyettinger added a commit that referenced this issue Sep 8, 2018
This is relevant for #16 . The implementation of appendMessage() for
String correctly added an item to the internal List of IColoredString
values, but appendMessage() for IColoredString did not. This should work
now.

I was considering adding a method to IMarkup to escape non-markup usage
of characters that might be mistaken for markup, but it doesn't seem
needed and would break anything that implements IMarkup. So, the code I
wrote is only present in comments in markup-related files.
tommyettinger added a commit that referenced this issue Sep 8, 2018
Most of the distance field fonts now include the full block image at
Unicode codepoint 0. That block was previously a separate Texture, and
that has performance downsides when mostly rendering text. By keeping an
entire text-based line as potentially one String with different colors,
we also open up possibilities for both optimizing our drawing a little
better, and for using markup on long Strings (as I'd like to have for
#16 )Though the fonts should keep the changes in this commit for later
(it gave me a chance to improve some unrelated spacing issues in some
fonts), TextCellFactory will have its changes in this commit immediately
reverted, since not all fonts have a block at codepoint 0, including
probably all user-created fonts. The solution is probably to use a
subclass of TextCellFactory for fonts we know have been made to use the
block in the font, and default to the normal separate texture for all
other fonts.
tommyettinger added a commit that referenced this issue Sep 8, 2018
This was directly inspired by #16 and Rakaneth's approach to generating
IColoredStrings by parsing GDX color markup; this way also uses regular
expressions, but uses RegExodus like the rest of SquidLib for GWT
compatibility. Some more-advanced and less-documented RegExodus features
are used here to simplify the code, at least for me since I've attempted
to understand RegExodus for a while now. This can generate colorful text
like at the bottom of this screenshot, https://i.imgur.com/CBsLzdL.png ,
using code like this:
```
IColoredString<Color> text = GDXMarkup.instance.colorString(
"Use numpad or vi-keys ([CW Bright Red]h[CW Bright Apricot]j[CW Bright
Yellow]k[CW Bright Lime]l" +
"[CW Bright Jade]y[CW Bright Azure]u[CW Bright Sapphire]b[CW Flush
Purple]n[]) to move. Use " +
"[CW Pale Indigo]?[] for help, [CW Faded Brown]f[] to filter colors, [CW
Gray White]q[] to quit. " +
"Click the top or bottom border of this box to scroll.");
```
This wrapper around GDX markup isn't the most convenient way of
specifying colors that I can think of, but it's pretty nice.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants