Revamping the Your Dry Delight glossary code and community events planned for the weekend


Events

We have several activities planned for this week!

Your Dry Delight glossary

The YDD glossary code was initially an ugly hack that used different screens for when you clicked on a word from the dialogue and when you looked it up in the glossary. While it worked, it was not pretty.

screenshot of commented out code for the tommy gun glossary screens. there are two screen definitions, with essentially the same content in each.

Two different screens defined for the same content

It was especially bad when we went to get YDD translated. To get that working, we had to append _language to every screen, make sure the translator knows how to add the _language to the translations, and duplicate the screen. If the translator wanted to add or remove words, then that would require adding extra conditionals to the sidebar with the list of words to ensure the wrong words in the wrong languages didn’t get all mixed up. The end result was 2 large glossary files per language that was difficult to debug for typos or strange behavior.

With the most recent language update, we decided to redo the glossary code to make it easier for future translations and just generally adding entries to a glossary (DMR’s glossary will be updated too). The glossary data is all stored in an external csv-like file, and then at the start of the game, we read the file and dynaimcally generate the glossary. For each language, we create a glossary.csv file. It has columns: language, key, entry, and definition. Language is unnecessary now, but it was originally created to have all the glossaries for every language combined into a single file. The key is for programming purposes; spaces are one of the greatest evils for coding, so we need a version of the word without any spaces (e.g. kid_gloves for “Kid gloves”). The key is also used to determine the order of the entries in-game. The word list is alphabetically ordered, so if there is a particular order that another language wants their words in, they can easily adjust that. The entry is the word to look up in the glossary, and the definition is the word’s (or phrase’s) meaning.

screenshot of english glossary.csv showing different columns and rows for all the glossary entries
example of how the russian keys are in abc order

The code for reading in the glossary:

init 5 python:
    # define a dictionary for each language to hold all the glossary entries for each language
    glossary_dict = {"french":{},"english":{},"russian":{},"chinese_simp":{}}
    
    # create a glossary entry class just for data management. 
    # not necessary if you just want to use anonymous tuples
    # but we enjoy objects
    class GlossaryEntry():
        def __init__(self, entry, definition):
            self.entry = entry
            self.definition = definition
    
    # when you click a word in dialogue to look it up in the dictionary,
    # open the glossary and show the word and its entry
    def set_glossary_entry_on_show(entry_key):
        store.active_entry=entry_key

        # if you are keeping track of whether the player has seen the word before
        # persistent.__dict__[entry_key]= True

        renpy.transition(dissolve)
        renpy.run(ShowMenu("glossary"))
        renpy.restart_interaction()
        return 

    # read the glossary.csv 
    def read_glossary():
        # for every language, open the appropriate glossary_language file
        for l in glossary_dict.keys():
            f = renpy.file("glossaries/glossary_"+l+".csv")
            lines = f.readlines()
            # skip the header row
            lines = lines[1:]
            col = 0
            # for every line in the file
            for line in lines:
                # split the line by the delimiter. Usually in a csv we use
                # a comma (csv = comma separated values),
                # but we use commas in normal text, so we have to use
                # some other delimiter. Choosing | is an arbitrary decision.
                # You can use any other delimiter as long as it doesn't interfere
                # with splitting the file up into different columns
                cols = line.split('|')

                lang = cols[0]
                key = cols[1]

                # Get rid of the extra \ when reading the file so that you
                # can have separate paragraphs in the word definition.
                # Decode utf-8 characters so you can read non-ascii characters 
                # like Chinese and Russian
                entry = cols[2].replace("\\n","\n").decode('utf-8')
                definition = cols[3].strip("\r\n").replace("\\n","\n").decode('utf-8')

                # Create a new glossary entry object to hold the data
                ge = GlossaryEntry(entry,definition)
                # Add the glossary data that you just read into the 
                # game's glossary for accessing later
                store.glossary_dict[lang][key] = ge

    # run the read_glossary() function to read in the glossary data
    read_glossary()

The Ren’Py screen code for actually accessing the glossary in-game

screen glossary():
    # This is the glossary that is accessible via the side menu
    zorder 199
    modal True
    tag menu

    if main_menu:
        add "gui/mainmenu.jpg"

    # this is the definition of the word once selected
    use glossary_single
    # this is the navigation containing all the words that you can open 
    use glossary_menu
# set the glossary scrollbar so that when you switch between words, the scrollbar
# resets to the top of the definition
default glosssary_scroll_value = ui.adjustment()

screen glossary_single():
    # the screen with the info after someone clicks a word to show the definition
    add "gui/menu.png"
    add "gui/glossary_title.png"
    imagebutton:
        idle "gui/clickoff.png"
        hover "gui/clickoff.png"
        action [Return(), Function(hide_nav), Hide("glossary_single"), Hide("glossary_menu"), Hide("glossary"),Hide("navigation"),Hide("menu")]      
        xpos 0
        ypos 0
    modal False
    
    # if the glossary has just been opened, then we don't want to display an entry's information
    if store.entry_data is None:
        $ store.entry_data = ""
    if store.entry_definition is None:
        $ store.entry_definition = ""
    tag menu
    zorder 200
    style_prefix "entry"
    vbox:
        ysize 320
        # the entry that was clicked on
        label store.entry_data  xpos 610  ypos 140 xalign 0.0 xsize 300
        viewport id "entry_vp":
            mousewheel True
            draggable True
            xpos 610
            ypos 159
            xsize 300
            yadjustment glosssary_scroll_value
            # the definition of the entry that was clicked
            text store.entry_definition
    vbar value YScrollValue("entry_vp") xpos 922 ypos 131 ymaximum 361 base_bar "gui/scrollbar/vertical_idle_bar.png" thumb "gui/scrollbar/vertical_idle_thumb.png" thumb_offset 21  top_gutter 25  bottom_gutter 15 right_gutter 20 left_gutter 10 xsize 42 
    use navigation

screen glossary_menu():
    zorder 205
    # when we first open up the glossary, we don't want an entry to already be showing
    $ store.active_entry = None
    # make sure we open the english dictionary entries if we are in english
    if _preferences.language is None:
        $ entry_lang = "english"
    else:
        $ entry_lang =  _preferences.language
    # if the player has selected an entry, then we should update the screen
    # to display the information for the selected entry
    if store.active_entry is not None:
        $ store.entry_data = glossary_dict[entry_lang][store.active_entry].entry
        $ store.entry_definition = glossary_dict[entry_lang][store.active_entry].definition
    $ glossary_keys_sorted = sorted(glossary_dict[entry_lang].keys())
    $ lang_dict = glossary_dict[entry_lang]
    vbox:
        xpos 318
        ypos 140
        xsize 210
        ysize 346
        spacing 23

        viewport id "g_list":
            mousewheel True
            draggable True
            vbox:
                style "list_text"
                spacing 13
                # dynamically create the list of words in the glossary
                for k in glossary_keys_sorted:
                    $ v = lang_dict[k]
                    textbutton v.entry action [SetVariable("store.active_entry",k),\
                    SetVariable("store.entry_definition",v.definition),\
                    SetVariable("store.entry_data",v.entry),\
                    Function(glosssary_scroll_value.change,0)] text_color "#262118" text_size 24 text_hover_underline True text_selected_idle_underline True text_idle_underline False
                    # textbutton glossary_dict[key].name action [SetVariable("store.active_entry",key),SetScreenVariable("entry_definition",glossary_dict[key].name),SetScreenVariable("entry_data",glossary_dict[key].entry)] text_idle_color "#1d1818" text_selected_idle_color "#818181" text_hover_color "#818181"

Questions or Comments?

Feel free to send in any AG-related questions! Our Ask Box is always open.

Q: This may be too early to ask but for the potential new VN competition idea, would you all consider only people with experience and with a finished script or would it be open to people with a concept and at least an outline as well?

A: If you have a concept and an outline, and you can provide evidence that you are able to see the project through and complete the script, then you would be a good fit for applying to the competition. There will be more information on the specifics and details required from outlines, as well as general specs for designs (max number of characters, backgrounds, etc.)!

Thanks so much for all of your amazing support, and stay safe out there!