• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Making dialog messages fit nicely

Status
Not open for further replies.
Level 15
Joined
Aug 7, 2013
Messages
1,337
Hi,

I'm using dialogs as a way of NPC talking. Here's a brief outline to explain my usage.

JASS:
local dialog d = CreateDialog()
local string npcMsg = "How may I help you?"
call DialogSetMessage(d, npcMsg)
call DialogAddButton(d, "Option1", ...)
...

Now this works for very short dialog messages. But if the NPC is telling the player information about the plot/universe/mythos/etc. a single liner isn't enough. What happens is because (for whatever reason) dialog boxes have a fixed size, longer strings will go out of the dialog box and possibly off screen.

The solution has been to use "\n" to try to split the message into blocks of words such that each line is exactly the width of the dialog box. This approach doesn't work unfortunately because sometimes the best "\n" would actually break up a word and that looks terrible.

Finding the best split isn't easy, and I'd rather not have to eyeball every dialog message until it looks perfectly.

I suppose I can make a very naive rule--it tries to fit the most words/characters to the width, but if it means breaking up a line, then I take however many words less. So basically I'm as greedy as possible, but I can't break up words (characters separated by whitespace). If that's the case, then I have to move them onto the next line.

I just didn't want to pre-process every string...

I mean I don't have to worry about this issue--people can just deal with the text being outside the box I suppose (would I lose points for this in a review?). But it doesn't look that good.
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
I don't plan on actually doing the transformation of the strings in game.

I'd just run them through a python script or something and use those generated strings.

Well I've basically made my whole NPC API around dialogs, so it's a bit hard to change now. But I made narratives their own struct ("Story") so what other options are there?

Keep in mind dialogs are handy because it lets me set quest objectives like "listen to the old man's story" or "find out what the priest knows." Completing those without dialogs is far too vague. My quest api relies in these simple events too.
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
Eh but then you can just click him from any distance. And clicking is a bit more marked in these situations. The method of interaction has been right clicking with your hero (issuing a smart order onto some target owned by the NPC) within some range (200 distance, face to face).

When I say marked I mean a player has to move to the NPC and then left click on it. It's more intuitive to right click on it, because that's the same command as moving to a point. I'm not qualified to make these statements though (we would need to do some experiments on whether players preferred right click + left click or right click + right click). But it feels better to my hand.

Back to the OP, I'm wondering what the "width" in characters is of a dialog. That is, what is the maximum number of characters that can be put on a line in a dialog without it flowing out of the dialog box or touching its edges. I did some testing and got a number of around 25 characters, but then it didn't work as expected.

So does each character take the same amount of room on the dialog?
ccccccccccccccccccc
GGGGGGGGGGGGGG

I guess the answer is no, because capital letters are "bigger" than lowercase ones.

I don't suppose there's a way to say how much of a line's width a given character takes up (i.e. its space). And perhaps these are uniform?

AAAAAAAb
GGGGGGGb

Here a capital G actually takes up more room than a capital A. So it's definitely based on each character width. Is there a reference for this? Or do I have to make my own metric up?

e.g. a dialog line has a width of 5 inches. All lower case characters take up 1/5 of an inch. Letter G takes up about 1.5x as much, while letter A takes up 1.125 as much.

If such metrics existed then I could do some easy maths to get the best fit for each line.

aab
AAb
 
Level 15
Joined
Aug 7, 2013
Messages
1,337
I ended up using a width for lower case, and a width for uppercase, both are approximations. I did not find the exact widths of every character, but the estimations are good enough.

Words are defined as a contiguous string of characters except whitespace. Whitespace separates words.

Approximately the dialog can fit about 28 character units per line. Lower case characters take about 1 character unit, while upper case characters take about 1.33 character units.

Using that I made a quick script that adds the new lines and padding automatically, so a dialog message will never overflow out of the box and looks justified/nice.

Some cautions are no words can exceed 28 character units. Probably unlikely one would have such a large word (unless we were speaking German or something :p), so I don't care to handle it. Also, don't use double spaces for separating punctuation. Always separate each word or sentence by a single white space. Anymore and you'll confuse the script.

The script is in Python below, in case anyone ever wants to use dialogs for speech. I didn't comment it because it's just a quick and dirty script.

Code:
#code to make text fit WC3 dialog width space
#each line has a width of about 28 'a' s
#each line ends with a '\n' character
#and must be padded by 5 spaces before hand + 4 spaces after

lowerWidth = 1

upperWidth = 1.33 #21 uppers fill a line, roughly

lineWidth = 28

frontPadding = "     "

afterPadding = "    "

def addLines(text, lineWidth = 28):
    words = text.split(" ")
    newText = ""
    newString = ""
    i = 0
    currWidth = 0
    while i < len(words):
        newString = ""
        currWidth = 0
        done = False
        x = i
        print "finished a loop"
        while not done:
            print x,i
            if getWordWidth(words[x]) + 1 + currWidth < lineWidth:
                newString += words[x] + " "
                currWidth += getWordWidth(words[x]) + 1
                if x + 1 == len(words): #reached the end
                    newString = frontPadding + newString
                    newString = newString + afterPadding
                    newString += "\n"
                    print newString
                    newText += newString
                    done = True
                    i = x + 1
                else:
                    x += 1
            else:
                newString = frontPadding + newString
                newString = newString + afterPadding
                newString += "\n"
                print newString
                newText += newString
                done = True
                i = x
    return newText

def getWordWidth(word):
    total = 0
    for x in range(0, len(word)):
        if word[x].isupper():
            total += upperWidth
        else:
            total += lowerWidth
    return total
 
Status
Not open for further replies.
Top