Inky Sample Code for the New External Calls

Discussion in 'Ink NPC Dialogue Composition' started by Owsley, Nov 15, 2021.

Tags:
Thread Status:
Not open for further replies.
  1. Owsley

    Owsley Bug Hunter

    Messages:
    346
    Likes Received:
    483
    Trophy Points:
    43
    Gender:
    Male
    The past week I've been upgrading my scripts to incorporate the use of the new external functions and variables. This thread will serve as a place to share what I've learned as well as code snippets and best practices.

    The following script contains a workaround for the editor failing when using calls to the external routines. I've created two functions (LoadData and SaveData) to centralize the external calls. The variable name is passed as a string and the call is made. This means all you need to do is comment and uncomment code in these two functions to test in the Inky editor or run in the game. When you have to load and save several different variables, the value of this will become obvious.

    Since calls to game data and variables will not work in the editor, I've initialize them to have some data in them for testing purposes in the editor.

    I also tend to create functions to use as Booleans to evaluate the saved data. This enhances the readability and gets rid of using the == operator all over the place. It also facilitates testing since you can easily change the Boolean condition in one place. Ideally my Boolean data should be saved as 0 and 1 but until the ability to save integer data is rolled to production, I chose to use the strings "NO" and "YES".

    Most of my scripts follow the design pattern illustrated below. I'll ask the player for their name somewhere in the dialog, save the fact I know their name, and greet them by name on subsequent visits.

    The "Welcome" line demonstrates the use of conditional text based on saved data. If I know the player name I address them by it, if not I call them Outlander. The format is {<Boolean condition>: <True text> | <False text>}

    Code:
    EXTERNAL saveVariable(varName)
    EXTERNAL loadVariable(varName)
    VAR currentScene = "some scene"
    VAR playerName = "some name"
    VAR playerNameKnown = "NO"
    
    ->MainStitch
    = MainStitch
    ~LoadData("playerNameKnown")
    
    Welcome to {currentScene}, {isNameKnown(): {playerName}|Outlander}.
    {isNameKnown():
        Much has happened since you were last here.
        ->Goodbye
    -else: ->EnterName
    }
    
    -(EnterName)
        What is your name?
        +[My name is {playerName}]
        ~playerNameKnown = "YES"
        ~SaveData("playerNameKnown")
        ->Goodbye
    
    -(Goodbye)
        Be sure to visit again.
    ->END
    
    ====function isNameKnown===
    ~return playerNameKnown == "YES"
    
    ====function LoadData(varName)===
    loading {varName}        //uncomment for testing in editor
    //~loadVariable(varName) //comment for testing in editor
    
    ====function SaveData(varName)===
    saving {varName}         //uncomment for testing in editor
    //~saveVariable(varName) //comment for testing in editor
     
    Last edited: Dec 9, 2021
  2. Owsley

    Owsley Bug Hunter

    Messages:
    346
    Likes Received:
    483
    Trophy Points:
    43
    Gender:
    Male
    I added some emotes and conditional responses based on the player's virtue;

    Code:
    EXTERNAL playEmote(emoteName)
    VAR VirtueTruth = 0
    .
    .
    .
    
     {VirtueTruth:
        - 0: Your aura is telling me that you sometimes struggle with the truth.
        - 1: You impress me as a kind soul and your heart seems true.
        - else:
                ~playEmote("TalkAngry1")
                While you were gone I made some inquires about you. It seems your reputation as a liar proceeds you. You expect me to believe such nonsense? GUARDS! Remove this scoundrel at once!
                ->END
    }
     
    Last edited: Dec 9, 2021
  3. Owsley

    Owsley Bug Hunter

    Messages:
    346
    Likes Received:
    483
    Trophy Points:
    43
    Gender:
    Male
    Here is a complete script I wrote for a new character. He's a proud butcher in the kitchen of my Harvest dungeon.

    The code contains several techniques that I've used in a lot of my conversations:
    • It's sharing data about the status of the main quest line in the player dungeon.
    • It detects if you have spoken to this character or other characters before and adjusts the dialog accordingly.
    • It plays different emotes.
    Note: The ? character is there because there is a bug with SOTA's implementation of Inky where lines of text are dropped. It's a dummy line of text that is required to get the dialog to display correctly. The dialog works without the ? in the editor, but at runtime in-game, the text is dropped. Also of note, I'm using "ow" to prefix my saved variables (Owsley). Initially I thought there might be name collisions with other player data and I decided to add a prefix based on my character's name. After finding the variables in the registry, they appear to have some sort of numeric id appended to them so there will be no collisions.

    Code:
    EXTERNAL playEmote(emoteName)
    EXTERNAL saveVariable(varName)
    EXTERNAL loadVariable(varName)
    VAR playerName = ""
    VAR owHQ="" //Harvest Quest Status
    VAR owCook=""
    VAR owButcher=""
    VAR nameKnown=0
    ->Main
    === Main
    ~LoadData("owHQ")
    ~LoadData("owCook")
    ~LoadData("owButcher")
    ~nameKnown = isKnown()
    {isKnown():
        ~emote("Talk")
        Why hello again {playerName}. No game? Oh well, we've all been on hunts and returned empty handed, haven't we. What can I do for you?
        ->_Menu
    }
    
    -(_Greeting)
    ~emote("Eat")
    Hold on, I'm finishing my lunch.
    +[Lunch?]
    ?
    Yes, a juicy leg of mutton. I've been so busy with all the preparations for the Ball, I have to grab a bite whenever I can. Now then, who are you?
        ++[My name is {playerName}]
        ?
        A pleasure to meet you {playerName}. {owHQ == "":You must be here for the ball.|How may I help you?}
        ~owButcher = "Y"
        ~SaveData("owButcher")
    
    -(_Menu)
    *[Name]->_Name
    *[Job]->_Job
    *{owHQ == "" || _Job}[Tell me about the ball]->_Ball
    *{_Ball}[Who is Mrs. Patmore?]->_Cook
    *{isQuestActive()}[Have you heard any crying?]->_Crying
    +[Thank you for your time]->_Bye
    
    ->END
    
    -(_Name)
     ?
    ~emote("Talk")
    {isNameKnown(): You forgot my name already? I'm Marty, but you can call me Butch.| My name is Marty, but my friends call me Butch.}
    ->_Menu
    
    -(_Job)
    ?
    ~emote("Pontificate")
    I'm the Butcher of Owsley Manor. There's not a creature in the land that I can't dress. Noble Stag, Obsidian Boar, even Pit Dragons; you name it, I can carve it up. The good news is you can taste it all at tonight's Ball. Whatever you hunt, I can turn into filets, racks of ribs, sausage, stew meat, even pet food. I waste nothing.
    ->_Menu
    
    -(_Ball)
    ~emote("Talk")
    ?
    It's the annual Harvest Moon Ball. Some of the tastiest beast in the land will be consumed tonight. Mrs. Patmore has been hard at work all day preparing the meat I cut for it. If you want to know more about the Ball I suggest you talk with Lady Owsley in the Foyer.
    ->_Menu
    
    -(_Cook)
    ~emote("Point")
    ?
    Mrs. Patmore? {isCookKnown(): I thought I saw you talking with her earlier. Maybe you didn't catch her name. She's| Why she's} the cook of Owsley Manor. Been here so long she's got to callin' it home. Makes the tastiest Roast Beast in the realm. She's over there preparing it now.
    ->_Menu
    
    -(_Crying)
    ?
    ~emote("ThroatCut")
    Listen, nothin' good ever comes from asking too many questions. His Lordship pays me to butcher his game and that's what I do. I mind my own business and suggest you do the same.
    ->_Menu
    
    -(_Bye)
    ~emote("Eat")
    ?
    {isNameKnown(): Nice talking with you again | Take care }{playerName}. Next time you visit, bring along some game from your hunt. I'll dress it up real nice for ya. Now then, back to my lunch.
    ->END
    
    ====function isQuestActive
    ~return owHQ == "A"
    
    ====function isKnown
    ~return owButcher == "Y"
    
    ====function isNameKnown
    ~return nameKnown == 1
    
    ====function isCookKnown
    ~return owCook == "Y"
    
    ====function emote(emoteName)===
    //play emote {emoteName} //uncomment for testing puposes
    ~playEmote(emoteName) //comment out this line for testing in editor
    
    ====function LoadData(varName)===
    //loading {varName}     //uncomment for testing puposes
    ~loadVariable(varName) //comment out this line for testing in editor
    
    ====function SaveData(varName)===
    //saving {varName}     //uncomment for tesing purposes
    ~saveVariable(varName) //comment out this line for testing in editor
    
    [​IMG]
     
    Last edited: Dec 9, 2021
  4. Owsley

    Owsley Bug Hunter

    Messages:
    346
    Likes Received:
    483
    Trophy Points:
    43
    Gender:
    Male
    My latest conversationalist is an Elven Watch Commander and I decided to use the NBPeriodOfDay exposed variable to customized the dialog to indicate if it is the daywatch or nightwatch. I built a Boolean function using the string contains operator "?" to determine if it is night or day. Please note that the operator is case sensitive so both "Night" and "night" have to be tested. The conversationalist can be found keeping watch on Owsley Island in Silverdale Market.

    Here is the code snippet;
    Code:
    VAR NBPeriodOfDay = ""
    
    -(_Job)
    I am the Commander of the {isNight(NBPeriodOfDay): Nightwatch | Daywatch}. I protect the Abbey and all those who visit.
    ->_MainMenu
    
    ====function isNight(s)
    ~return s ? "night" || s ? "Night" || s ? "Dusk" || s ? "Evening"
    
    
    
     
    Coswald_Dirthmire and Anpu like this.
Thread Status:
Not open for further replies.