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

Dialoger and FontEngine task distribution. #60

Open
Gankra opened this issue Sep 5, 2012 · 42 comments
Open

Dialoger and FontEngine task distribution. #60

Gankra opened this issue Sep 5, 2012 · 42 comments
Labels

Comments

@Gankra
Copy link
Member

Gankra commented Sep 5, 2012

The idea has come up of creating a generic API so that:

(a) The way conversations are displayed
(b) The way text formatting is parsed
(c) The way text is rendered

Are split up into three separate replaceable modules. Don't like how we render text? Swap it out. Don't like our syntax? Swap it out. Want conversations to just be alerts? Swap it out.

As it stands, (a) is handled by the Dialoger class for the most part. However it performs some of the roles of (b). For example it parses the text into "batches" (the @ delimiters).

(b) and (c) are handled by FontEngine, however for the most part, FontEngine is already broken up into essentially two parts, the parser and the renderer, via the parser handing the rendering code FormatRange objects, that describe what to render. However it ultimately is one object and shares a bunch of stuff.

The questions would then be:
Is it worth it?
Should the batch parsing be moved out of Dialoger, and/or Dialoger split into two objects (batch parser, and batch renderer)?
Should FontEngine be broken into two classes, FontParser and FontRenderer?
Also how would this be architected?

With not much thought it would make sense to me to do it something like:
DialogParser has a DialogRenderer has a FontParser has a FontRenderer, and the core engine basically just dumps the text to DialogParser and calls it a day. Similarly the command prompts just go straight to FontParser to handle themselves, I guess.

@ikkonoishi
Copy link

I'm going to put this here as well as that forum post. Basically I'm thinking we take the base dialog text and shove it into the parser to get back the following, and then shove that into the renderer to show it on screen.

    //I'll use this line as a template throughout the exercise
    //@aradia_talk:#even-though-'time'-is-really-just-a-figure-of-speech-here~dialogimg_kankri1 ARADIA: anyway those of us who are concerned with the _preservation_ of /0xFF0000/rea/0x00FF00/li/0x0000FF/ty/0x/ have been looking forward to this day for some time
    class DialogLine
    {
        array meta[]; 
        //Contains metadata for this line.
        // ["@"] (Actor)    "aradia"
        // [":"] (Hashtags) "#even-though-'time'-is-really-just-a-figure-of-speech-here"
        // ["~"] (Background)   "dialogimg_kankri1"

        string text;
        //Contains the base text of the line shorn of formatting strings
        // "ARADIA: anyway those of us who are concerned with the preservation of reality have been looking forward to this day for some time"

        2darray format[][]
        //Contains the formatting for the text at any change
        //Possible formatting
        //  Currently used
        //      _       = toggles underlining
        //      /0xHEXHEX/  = change future text to "HEXHEX" hexadecimal color code
        //      /0x/        = change future text to the ("@")actor's color code
        //  Possible (Might be cool)
        //      /S:N/       = changes the "typing" speed 

        // [0]["index"] 0
        // [0]["font"]  "SburbFont"
        // [0]["color"] "ariadaRed"
        // [0]["underline"] false
        //
        // [1]["index"]     54
        // [1]["font"]      "SburbFont"
        // [1]["color"]     "ariadaRed"
        // [1]["underline"] true
        //
        // [2]["index"]     67
        // [2]["font"]      "SburbFont"
        // [2]["color"]     "ariadaRed"
        // [2]["underline"] false
        //
        // [3]["index"]     71
        // [3]["font"]      "SburbFont"
        // [3]["color"]     "FF0000"
        // [3]["underline"] false
        //
        // [4]["index"]     74
        // [4]["font"]      "SburbFont"
        // [4]["color"]     "00FF00"
        // [4]["underline"] false
        //
        // [5]["index"]     76
        // [5]["font"]      "SburbFont"
        // [5]["color"]     "0000FF"
        // [5]["underline"] false
        //
        // [6]["index"]     78
        // [6]["font"]      "SburbFont"
        // [6]["color"]     "ariadaRed"
        // [6]["underline"] false
    }

@Gankra
Copy link
Member Author

Gankra commented Sep 5, 2012

Pre-existing FormatRange object:
Sburb.FormatRange = function(minIndex,maxIndex,type,extra){
this.minIndex = minIndex;
this.maxIndex = maxIndex;
this.type = type;
this.extra = typeof extra == "string"?extra:"";
}

extra would hold, e.g. the colour to use if the type was colour.

@ikkonoishi
Copy link

[QUOTE=Mme Sparklecakes XVII;6676646]What's wrong with the current FormatRange object, as far as styles go? More generalized, and you can add/ignore them whenever.[/QUOTE]

Basically what I'm seeing as the problem is that they are broken apart when at any time you just want to know what font you should be applying to the text. You have to look at each range to decide what style to apply to the current letter.

With my method adding a letter looks like.

    var curFormat = 0;
    for (var curLetter = 0;curLetter<text.chars.length;curLetter++)
    {
        if (formats.length>curFormat+1&&formats[curFormat+1].index<=curLetter)  
            curFormat++;
        addLetter(text.chars[curLetter],formats[curFormat]);
    }

While yours is slightly more complicated since you have to loop through the entire formatting array to find all possible applicable formats on each letter. Then you need to pull up your current formatting, and make changes. My way replaces all formatting only when something changes which is somewhat quicker.

    var curFormat = new Object();
    for (var curLetter = 0;curLetter<text.chars.length;curLetter++)
    {
        for(var i = 0;formats.length>=i;++;)
        {   
            if (curLetter>=formats[i].minIndex&&curLetter<=formats[i].maxIndex)
                curFormat[formats[i].type]=formats[i].extra;
        }
        addLetter(text.chars[curLetter],curFormat);
    }

@Gankra
Copy link
Member Author

Gankra commented Sep 5, 2012

My way should be done as follows:
Put all FormatRanges in a priority queue (sorted by minIndex), actually could just be a sorted list since this one is fairly static.
When a FormatRange is entered, put it on another priority queue (sorted by maxIndex)

As a bonus, you don't have to walk through every character and ask "okay, now?", you just have to look at the heads of the two queues and ask "when does my current style change?", and just jump to that. That means I can also render large blocks of text at once (generally a whole visual line, if there's no formatting on it), instead of character by character. I don't even need to consider characters which have no unique style.

I'm doubtful this is how I actually implemented it, because I rushed it.

Also no maintenance of an extra array the size of the the string (though you could replace your array with a map, honestly).

@ikkonoishi
Copy link

Hmm yeah. I guess they would be about the same then. Honestly for the most part I just didn't see it way down there at the bottom until I had already gutted the rendering parts.

For lines that only contain a single type of formatting my array would only have a single entry. If you weren't in the "typing" style of placement you would just grab the substring from the current format entry until the next one, plop them in a span, and set the span's style to whatever the format was.

I guess the only major thing that makes me prefer my method is the fact that since I would be rendering with html something like "_Underline\0xaaaaaa\ColorChange_NotUnderlinedButStillColorChanged\0x" could mess up tag nesting.

I'm probably over thinking it really.

@Gankra
Copy link
Member Author

Gankra commented Sep 6, 2012

My syntax explicitly supports unbalanced tags.

@Gankra
Copy link
Member Author

Gankra commented Sep 6, 2012

A compromise would be to return objects that specify the complete formatting at a point in the text, but only for the characters where the formatting actually changes. This way, the fact that my syntax supports unbalanced tags is obfuscated away, but it still supports my ability to just ask "when will it change next". Also no need to maintain two priority queues and a stack of active effects. All you need to do is ask "when will the style change next and to what", and render all the text up until that point with the current styles, without doing anything extra.

@ikkonoishi
Copy link

A compromise would be to return objects that specify the complete formatting at a point in the text,
but only for the characters where the formatting actually changes. This way, the fact that my syntax
supports unbalanced tags is obfuscated away, but it still supports my ability to just ask "when will it change next".

This is exactly what my thing does. Whenever the formatting changes it stores the current formatting, and the index the change occurs at.

Also no need to maintain two priority queues and a stack of active effects. All you need to do is ask "when will the style change next and to what", and render all the text up until that point with the current styles, without doing anything extra.

Not sure what you are saying here, but it is time for me to head to work so I'll look at it again in the morning.

@Gankra
Copy link
Member Author

Gankra commented Sep 6, 2012

I got the impression from your description that you stored it for every character.

The second half I was discussing there was that my method is just heavy on datastructures.

@LtSquigs
Copy link
Member

LtSquigs commented Sep 6, 2012

So is the plan to split the Dialogger up into different parts now? If so it might be worthwhile to create an issue for each part so multiple people can work on them simultaneously without stepping on toes.

@Gankra
Copy link
Member Author

Gankra commented Sep 6, 2012

That would seem to be the plan, but we're trying to settle how much each part should do, and how they should communicate.

@FugiTech
Copy link
Member

FugiTech commented Sep 6, 2012

2 parts for all of text handing. One parts loads from the file and generates a completely generic object to be passed to the other part for doing whatever it takes to display it.

@Gankra
Copy link
Member Author

Gankra commented Sep 6, 2012

Right, but does the parsing object handle parsing conversations into
"windows", or is that still a job for the Dialoger class?

@LtSquigs
Copy link
Member

LtSquigs commented Sep 6, 2012

I think 'windows' should be handled by the parsing. After all, I think the goal would be that, for whatever language you use, you should get the same thing rendered regardless of if you use XML or CrazyAndrewFormat. (If we allow multiple rendering engines, than at least consistency when switching languages on the same renderer).

@FugiTech
Copy link
Member

FugiTech commented Sep 6, 2012

By "windows" do you mean the automatic splitting of huge lines? If so the Dialoger should handle that, as windows may not be consistent in size or wrap method, resulting in different presentation.

I think ideally the parser just gives you the lines, and the renderer figures out how it has to break it up and apply the styling. Changing the renderer will change how it looks in more ways then the internals. Changing the parser shouldn't matter.

@Gankra
Copy link
Member Author

Gankra commented Sep 6, 2012

Right so Dialoger needs to be broken up as well too, then. And the parser
part of FontEngine needs an expanded api so Dialoger can still know what's
going on.

@Gankra
Copy link
Member Author

Gankra commented Sep 6, 2012

By "windows" I mean the @'s delimiting a new sprite/window.

@Gankra
Copy link
Member Author

Gankra commented Sep 6, 2012

I'm probably the most qualified to know what the hell it's supposed to be
doing at the end of the day. I might start tackling this refactor some time
tomorrow.

@FugiTech
Copy link
Member

FugiTech commented Sep 6, 2012

Oh, then yes the parser makes the "windows", parses the styling, etc etc.

The renderer has to figure out what to do if the text is too long, figure out how to take the (now generic) style information and make the text actually styled (or ignore it if it doesn't know how).

The idea is that there should be no need for any communication between parser and renderer other than "Hey here are the lines", and that any parser can work with any renderer.

@Gankra
Copy link
Member Author

Gankra commented Sep 6, 2012

Maybe I'll make FontEngine be a third class that wraps FontParser and
FontRenderer. That way nothing has to juggle two objects.

@ikkonoishi
Copy link

Why not take all the text and UI elements, and just put them in a div floating over the canvas? None of them update fast enough or do anything fancy enough to justify manually redrawing them every frame.

I guess I'm kind of starting to sound like a broken record on this issue, but just hear me out.

We have an xml file call it uiElements.xml or something.

<sburb>
<ui>
<elements>
<element name="sound" position="5,5,50,50" command="toggleVolume" background="volumeControl"/>
<element name="leftActor" position="whatever"/>
<element name="rightActor" position="whatever"/>
<element name="chat" position="whatever"/>
<element name="hashtags" position="whatever"/>
<element name="endConversation" position="whatever" command="skipDialog">
&gt; End Conversation.
</element>
<element name="data" position="whatever"/ "command="showSaveLoad"/>
<element name="pauseScreen" position="0,0,100%,100%" command="resumePlaying">
<center>
PUASED<br/>CLICK MOUSE TO RESUME<br/>FROM BELOW
</center>
</element>
<element name="chooser" position="whatever">
<select id="chooserSelectBox" size=5>
<option>The options will be replaced when the chooser is shown.</option>
</select>
</element>
</elements>
<states>
<state name="walkaround" show="canvas,sound,data,controls" setfocus="canvas"/>
<state name="dialog" show="sound,leftActor,rightActor,chat,endConversation" setfocus="chat"/>
<state name="pause" show="pauseScreen"/>
<state name="choose" show="chooser" setfocus="chooser"/>
</states>
</ui>
</sburb>

So we have that right? Okay now for each of those elements we create a div which is nested inside the main SBURBgameDiv. We transform the internal stuff like having command="toggleVolume" in the sound control thing into javascript event declarations like onclick="Sburb.performAction('toggleVolume')" or whatever it would be. The rest of the attributes will be passed on to the div so you can set CSS stuff here or later in code.

Only the elements listed in the show section of the current state will have their div shown. The "canvas" is exempt from being actually hidden, but it stops responding to events when it isn't shown in the current state.

The setfocus thing determines what segment recieves keypress events. Every div should have a getfocus event so if they aren't currently supposed to be visible they will just put focus back to the current setfocus target of the state they are in after firing whatever their command is if they have one.

As for the dialogs.
We can leave the definition alone really. People can add inline html tags to include things like images or url links, and they will be parsed properly if we are using the divs. We might want to wrap CDATA tags around them.

<action class='meenahTalk1' sprite='latula' command='talk' name='Talk to Meenah.'><args>
      @latula_happytalk LATULA: yo yo, p4ystubz my grrrl!
      @meenah_idle MEENAH: shit tules
      @meenah_talk:#passin-out-names-like-cheap-cuttlefish MEENAH: i forgot how many rad nicknames you like to cycle through
      @meenah_fish MEENAH: you know i always thought paycheck was kind of dope why dont you just stick with that
      @latula_happier:#WOO LATULA: r1ght on! 1 l1k3 th4t on3 too, p4ych3ck 1t 1z. H1GH F1V3 GRL!!!
      @meenah_angry:#OOW MEENAH: no lets not OWWWWWW
      @! -SNIP- 
      <!-- -->
</args></action>

I think this is a good setup for a dialog data format.

function dialog(text)
{
    this.lines = new Array();

    text = text.split("\n");
    for (var i = 0;i<text.length;i++)
    {

        var line = text[i].match(/(@[^:\s]+)(:[^\s]*)?\s([^\n]*)/);
        alert(text[i] + line);
        if (!line)break;
        var metadata = line[1].match(/([@_~%][^@_~%]+)/g);
        if (line[2])metadata.push(line[2]);
        //Metadata types can be determined by the first character
        // @ = actor
        // _ = sprite
        // ~ = textbox image
        // % = background image
        // : = extra info/hashtags (must be last)
        // We might want to add a metadata type for not parsing for formatting 
        // so people don't have to do a lot of underscore escaping
        this.lines.push({meta: metadata, text: line[3], formatting = null});
    }
}

function parseFormatting(dialog)
{
    for (var i = 0;i<dialog.lines.length;i++)
    {
        var formattinginfo = new Array();
        //Parse dialog.lines[i].text to extract the formatting into formattinginfo
        //Strip out formatting codes from dialog.lines[i].text, and remove escape characters
        //Set dialog.lines[i].text to the unformatted text
        dialog.lines[i].formatting = formattinginfo;
    }
}


//Example usage
var text = "@latula_happytalk LATULA: yo yo, p4ystubz my grrrl!\n      @meenah_idle MEENAH: shit tules\n      @meenah_talk:#passin-out-names-like-cheap-cuttlefish MEENAH: i forgot how many rad nicknames you like to cycle through\n      @meenah_fish MEENAH: you know i always thought paycheck was kind of dope why dont you just stick with that\n      @latula_happier:#WOO LATULA: r1ght on! 1 l1k3 th4t on3 too, p4ych3ck 1t 1z. H1GH F1V3 GRL!!!";      

var latMeeDia = new dialog(text);
parseFormatting(latMeeDia);

So much typing. Nearly bedtime. Hands sore.

@Gankra
Copy link
Member Author

Gankra commented Sep 6, 2012

Because I'm burnt out on trying to render text with someone else's
generalized textbox framework. I didn't feel div gave me the level of
flexibility I wanted, and if it did, it would be an uphill battle. This is
why I am refactoring the API, so that you can replace the way text is
handled if you want. Also pushing pixels right onto the canvas is supremely
compatible. If someone has weird plugins, or drop the system into a weird
layout, suddenly your text-box is fucked. Mine's like "the fuck is css"?

Also, pulling things out of the canvas makes shit waaaay more complicated.
Right now everything is sprites with animations. It's nice and easy for the
engine to understand. It doesn't give a shit what the Dialoger is doing.

The HUD elements are already completely replaceable in SburbML. Everything
about them visually, and functionally, is completely described in the
SburbML. Almost everything about the Dialoger is too.

Swapping out the text rendering engine is NOT a standard use case. Neither
the end-user nor the developer should really give a rats ass (beyond the
syntax used). The FontEngine is basically the last bastion of hard-coded
non-configurability under the tyranny of SburbML. I am hoping to change
that. But if you want to change the internals, it's an open source project
buddy, go nuts. If you don't like how we do things, you're free to fork. If
you think we'd like your way, feel free to submit a pull request.

Also it seems all of us either have school or jobs to deal with. So we
can't dedicate our whole days to this.

@FugiTech
Copy link
Member

FugiTech commented Sep 6, 2012

But hey, if you find a way to make a living off of this let me know.

@Gankra
Copy link
Member Author

Gankra commented Sep 6, 2012

I'm actually sustained by yelling on the internet.

@ikkonoishi
Copy link

I may have a tendency to get really fucking excited about whatever I start to do for the first few days. Possibly. Could be.

I get you on the fork issue. Think I'll do that for a bit.

You might want to look up where I edited in that wall of text and code to the last section. It actually has nothing to do with divs whatsoever. Just drop in the formatting decoder of your choice, and it is good to go.

By the way the wiki is kind of screwed up because it still is pointing to Gankro\Jterniaborn links. I fixed the home page. All I had to do was open it and save it, and it was fine.

@Gankra
Copy link
Member Author

Gankra commented Sep 7, 2012

I have submitted a tentative specification in #70

@Gankra
Copy link
Member Author

Gankra commented Sep 9, 2012

Specification is over here now: https://github.com/Gankro/Sburb/blob/text-engine/TextEngine.js

I do not recommend integrating this work into master until there is working code to substitute the current structure.

I will also not be doing further work on this effort as it simply does not interest me. The current system, while sloppy, is adequate for my purposes at this juncture in time.

@FugiTech
Copy link
Member

FugiTech commented Sep 9, 2012

The only reason I want this change is so that the editor can spit out XML instead of the current dialog format. I think it would be easier to export that way.

Still extremely low priority.

@Gankra
Copy link
Member Author

Gankra commented Sep 9, 2012

I wouldn't really see the editor as the ideal place to write the dialog
anyway. I guess I can see doing it, but room building is the primary goal
for me on that. Being able to visually place elements would be a huge boon.
Right now my workflow amounts to checking coordinates in GIMP and writing
them down in a book.

@ikkonoishi
Copy link

Have you looked into using an svg editor like inkscape? Import the sprite, put it where you want, open the object properties, set the id to the name, set the label to the class, and put whatever goes inside it into the description.

Then you can just write an xslt to turn it into whatever you want.

I'll keep plodding along with the text engine. I'm about 70% done with the parser. I'm thinking of moving all batching into the renderer because the parser has no need to worry itself about how shit fits on screen.

If you don't mind too much I want to shift around a little about how the data is stowed and passed internally, and maybe in the markup.

By preference it would be something like

<dialog>
<box name='talk' background='dialogBox'>
<style name='speakerLeft' position='90,120' textDimensions='150,30,350,220' spritePos='-300,450;100,450'/>
<style name='speakerRight' position='20,120' textDimensions='30,30,350,220' spritePos='950,450;550,450'/>
<style name='hidden' position='-1000,120'/>
<style name='alert' position='56,120' textDimensions="30,30,450,220"/>
</box>
<actor name='meenah' frameInterval='6' x='-180' y='-524' font="color: #77003c;" actortags="meenah;">
<sprites>   
   <animation name='_idle' sheet='meenah_idle'/>
   <animation name='_talk' sheet='meenah_talk' length='2'/>
   <animation name='_angry' sheet='meenah_angry'/>
   </sprites>
</actor>
<actor name='ariada' frameInterval='6' x='-245' y='-504' font='color: #a10000;' actortags='ariada;aa;'>
    <animation name='_happy' sheet='aradia_happy' />
<animation name='_happytalk' sheet='aradia_happytalk' length='2' />
</actor>
</dialog>

Right now I'm testing it with an html page which is working it's way up to be a basic dialog editor in it's own right.

@ikkonoishi
Copy link

Oh while we are talking about workflows. What javascript ide are you guys using. I just started using aptana, but it is kind of clunky, and doesn't seem to want to push to git.

Huge improvement over wordpad and notepad though.

@FugiTech
Copy link
Member

GEdit and command line git are all you need.

Well, all you need if you are too lame to write with a magnetized needle.

Also, breaking XML back-compatibility seems like a really, really bad idea...

@Gankra
Copy link
Member Author

Gankra commented Sep 10, 2012

Nah the markup is fine as it is. Leave it be for now. Also the renderer already handles the vast majority of batching. All format parsing must be in the parser, or you're just writing FontEngine again.

The parser already has no notion of how things "fit". It just specifies "I definitely want the text broken up AT LEAST here, but if you want more break points than go nuts".

@ikkonoishi
Copy link

So what are you calling batching?

Is non-batched

@Bluh_bluh Huge bitch
@Bluh_bluh Huge bitch
@Bluh_bluh Huge bitch

While batched would be

@Bluh_bluh Huge bitch\nHuge bitch\nHuge bitch

???

@Gankra
Copy link
Member Author

Gankra commented Sep 10, 2012

Non batched is just straight up text.

e.g. "Blha blah blah"

as opposed to

"@what_huh blha blha blah"

Chooser would use non-batched. Actually might just use no-format.

@ikkonoishi
Copy link

But if we are parsing why would we ever return the @_~: tags?

I strip all that off, and shove it in the various data orifices in TextParser or what have you. actors[]backgrounds[] ect

Not sure where to get boxes[] from

I need to stop working on it, but internet dude is still wireing everything so can't sleep yet. If I keep coding my incoherence will be come unreadable code.

@Gankra
Copy link
Member Author

Gankra commented Sep 10, 2012

none mode: Do not interpret any syntax. Return the string verbatim with
default stylings applied.
no_batch mode: Do not interpret batch-related syntax (in my case, that
would be not looking for @'s and the prefix stuff), but still parse inline
styles (_'s, #00ff00's...)
full mode: Interpret this as a batch of dialogs (in my case, do look for
@'s and the prefix stuff), and also parse inline styles.

Given:
"@karkat_idle what what what! @karkat_angry who?"
none mode:
return "@karkat_idle what what what! @karkat_angry who?"
no_batch mode:
return "@karkatidle what what what! @karkatangry who?"
full mode:
return "what what what!", "who?"

@Gankra
Copy link
Member Author

Gankra commented Sep 10, 2012

Minor corrections, no_batch should have had all the underscores parsed out.

@Gankra
Copy link
Member Author

Gankra commented Sep 10, 2012

boxes[] is the set of custom boxes to use on specific batches. If the default is to be used jut put null at that index. That stuff's described in the wiki page on dialogs.

Dialoger did a lot of stuff.

@ikkonoishi
Copy link

That stuff's described in the wiki page on dialogs.

Pretty sure it isn't.

Unless are boxes the ~tags?

@Gankra
Copy link
Member Author

Gankra commented Sep 11, 2012

Right in there:
@Karkat_Angry~background%box:#sweetPost
background is background
box is box
Karkat is actor
Karkat_Angry is animation
#sweetPost is extra

@ikkonoishi
Copy link

Box, what box?

Okay yeah my bad. Got that now.

https://github.com/ikkonoishi/Sburb/tree/textengine
Grab the text engine test bed html file and TextEngine.js, and see if the batching and styling are right.

I need to do more work on the renderer, and then I will start digging into the dialoger to get it working with the rest of the framework.

@Gankra Gankra mentioned this issue Oct 10, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants