1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. The Aftermath has been revealed for the 19th Terraining Contest! Be sure to check out the Results and see what came out of it.
    Dismiss Notice
  3. Melee Mapping Contest #3 - Results are out! Congratulate the winners and check plenty of new 4v4 melee maps designed for this competition!
    Dismiss Notice
  4. The winners of our cinematic soundtrack competition have been decided! Step by the Music Contest #11 - Results to check the entries and congratulate the winners!
    Dismiss Notice

Dialogue System

Submitted by Chaosy
This bundle is marked as pending. It has not been reviewed by a staff member yet.
[​IMG]

A remake of a GUI system I uploaded a few years back. This is easier to use and has way more functionality.

Code


Dialogue.wurst | KeyPress.wurst

Code (WurstScript):

package Dialogue

import LinkedList
import Table

@configurable
string selectColor = "|c00FFFFFF"
string defaultColor = "|c007d7d7d"
string numberColor = "|c00FFFF00"

integer allowedRows = 6

@endconfigurable

public Dialogue array dialogues
public Option triggeredOption
public player triggeringPlayer

public function getTriggeredOption() returns Option
    return triggeredOption

public function getTriggeringOptionPlayer() returns player
    return triggeringPlayer

public function getTriggeredDialogue() returns Dialogue
    return triggeredOption.parent

public function transmission(unit u, player target, string text, real dur)
    TransmissionFromUnitWithNameBJ(bj_FORCE_PLAYER[target.getId()], u, GetUnitName(u), null, text, bj_TIMETYPE_SET, dur, false)

function transmission(unit u, LinkedList<player> target, string text, real dur)
    force f = CreateForce()
    for player p in target
        f.addPlayer(p)
    TransmissionFromUnitWithNameBJ(f, u, GetUnitName(u), null, text, bj_TIMETYPE_SET, dur, false)
    f.destr()

function unit.transmission(LinkedList<player> target, string text, real dur)
    transmission(this, target, text, dur)

public function unit.transmission(player target, string text, real dur)
    transmission(this, target, text, dur)

public class Option
    string text
    string resp
    real respDuration
    boolean quit = false
    boolexpr cb

    Dialogue dialogue
    Dialogue parent

    construct(Dialogue parent, string text)
        this.text = text
        this.parent = parent

    function linkTextReply(string s, real duration) returns thistype
        resp = s
        respDuration = duration
        return this

    function linkDialogue(Dialogue d) returns thistype
        dialogue = d
        return this
       
    function makeQuit() returns thistype
        quit = true
        return this

    function callback(code c) returns thistype
        cb = Condition(c)
        return this

public class Dialogue
    Table hash = new Table() //used to contain player specific data

    LinkedList<Option> options = new LinkedList<Option>()
    LinkedList<player> users = new LinkedList<player>()

    unit target

    thistype prev

    construct(unit u)
        target = u

    function getStarterUnit(integer i) returns unit
        return hash.loadUnit(i)

    function getStarterUnit(player p) returns unit
        return getStarterUnit(p.getId())
   
    function setStartUnit(player p, unit u)
        setStartUnit(p.getId(), u)

    function setStartUnit(integer id, unit u)
        hash.saveUnit(id, u)

    function addOption(string text) returns Option
        Option o = new Option(this, text)
        options.add(o)
        return o

    function addPlayer(player p)
        addPlayer(p, null)

    function addPlayer(player p, unit u)
        integer id = p.getId()

        if(u != null)
            setStartUnit(id, u)

        users.push(p)
        dialogues[id] = this
        CinematicModeBJ(true, bj_FORCE_PLAYER[id])
        SetUserControlForceOn(bj_FORCE_PLAYER[id])
        transmission(u, p, toString(p), 9999)
        SetCameraTargetControllerNoZForPlayer(p, target, 0, 0, true)

    function swapTo(thistype d, player p)
        integer id = p.getId()
        unit origin = hash.loadUnit(id)

        users.remove(p)
        dialogues[id] = d

        d.users.push(p)
        d.hash.saveUnit(id, origin)
        transmission(origin, p, d.toString(p), 9999)
        SetCameraTargetControllerNoZForPlayer(p, d.target, 0, 0, true)

    function swapTo(thistype d, player p, boolean forward)
        if(forward)
            d.prev = this

        this.swapTo(d, p)

    function removePlayer(player p)
        users.remove(p)
        dialogues[p.getId()] = null
        CinematicModeBJ(false, bj_FORCE_PLAYER[p.getId()])
        hash.flush()

    function toString(player p) returns string
        string toReturn = ""
        integer selection = hash.loadInt(p.getId())
        int i = options.size() > 5 ? selection : 0
        int endLoop =  i + allowedRows >= options.size() and options.size() >= allowedRows ? i + allowedRows : options.size()
        while i < endLoop
            if i >= this.options.size()
                toReturn += numberColor + (i - options.size() + 1).toString() + "|r. " + (i == selection ? selectColor + options.get(i - options.size()).text + "|r" : defaultColor + options.get(i - options.size()).text + "|r") + "\n"
            else
                toReturn += numberColor + (i + 1).toString() + "|r. " + (i == selection ? selectColor + options.get(i).text + "|r" : defaultColor + options.get(i).text) + "|r\n"
            i++
        return toReturn

function disableTransmissionSkip()
    if bj_cineSceneBeingSkipped == null
        TryInitCinematicBehaviorBJ()
   
    DisableTrigger(bj_cineSceneBeingSkipped)

init
    disableTransmissionSkip()
 
Code (WurstScript):

package KeyPress

import Dialogue
import TimerUtils

trigger callback
enum Key
    up
    down
    right
    left
    invalid

function boolexpr.execute()
    TriggerClearConditions(callback)
    TriggerAddCondition(callback, this)
    TriggerEvaluate(callback)

function eventid.toKey() returns Key
    Key toReturn = invalid
    if this == ConvertPlayerEvent(267)
        toReturn = up
    else if this == ConvertPlayerEvent(265)
        toReturn = down
    else if this == ConvertPlayerEvent(263)
        toReturn = right
    else if this == ConvertPlayerEvent(261)
        toReturn = left
    return toReturn

function trigger.regKeyEventForPlayers(integer id)
    int i = 0
    while i < bj_MAX_PLAYERS
        TriggerRegisterPlayerKeyEventBJ(this, players[i], bj_KEYEVENTTYPE_DEPRESS, id)
        i++

function unit.makeResponse(player target, string msg, real duration)
    this.transmission(target, msg, duration)
    CreateTimer()
    ..setData(target.getId())
    ..start(duration) ->
        timer t = GetExpiredTimer()
        integer id = t.getData()
        Dialogue d = dialogues[id]
        transmission(getOrigin(d, players[id]), players[id], d.toString(players[id]), 999)
        t.release()

function getOrigin(Dialogue d, player p) returns unit
    return d.hash.loadUnit(p.getId())

function getSelection(Dialogue d, player p) returns integer
    return d.hash.loadInt(p.getId())

init
    callback = CreateTrigger()
    CreateTrigger()
    ..regKeyEventForPlayers(bj_KEYEVENTKEY_UP)
    ..regKeyEventForPlayers(bj_KEYEVENTKEY_DOWN)
    ..regKeyEventForPlayers(bj_KEYEVENTKEY_RIGHT)
    ..regKeyEventForPlayers(bj_KEYEVENTKEY_LEFT)
    ..addAction() ->

        eventid id = GetTriggerEventId()
        player p = GetTriggerPlayer()
        Dialogue d = dialogues[GetPlayerId(p)]

        if(d != null)
            integer selection = getSelection(d, p)

            switch id.toKey()
                case up
                    selection  = selection <= 0 ? d.options.size() -1 : selection - 1
                    d.hash.saveInt(p.getId(), selection)
                    transmission(getOrigin(d, p), p, d.toString(p), 999)

                case down
                    selection = selection >= d.options.size() -1 ? 0 : selection + 1
                    d.hash.saveInt(p.getId(), selection)
                    transmission(getOrigin(d, p), p, d.toString(p), 999)

                case right
                    Option o = d.options.get(selection)
                    triggeredOption = o
                    triggeringPlayer = p
                    o.cb.execute()
                    if(o.resp != null and o.resp != "")
                        d.target.makeResponse(p, o.resp, o.respDuration)

                    else if o.quit
                        d.removePlayer(p)

                    else if o.dialogue != null
                        d.swapTo(o.dialogue, p, true)
                case left
                    if(d.prev != null)
                        d.swapTo(d.prev, p, false)
                case invalid
                    print("Error") //Should never happen
 

Demo


Code (WurstScript):

package Hello

import Dialogue
import Talk

Dialogue array dialogues

function giveItem()
    Dialogue d = getTriggeredDialogue()
    player p   = getTriggeringOptionPlayer()
    unit u     = d.getStarterUnit(p)

    u.addItem('bgst')

init
    // Unit Preperation
    unit questNPC = createUnit(players[0], 'hfoo', vec2(100, 100), angle(0))
    ..setColor(PLAYER_COLOR_BLUE)

    // Dialogue setup
    dialogues[0] = new Dialogue(questNPC)
    dialogues[1] = new Dialogue(questNPC)

    dialogues[0].addOption("Hello, fellow soldier!").linkTextReply("Dues vult, brother. Here is a free item, check your inventory.", 4).callback(function giveItem)
    dialogues[0].addOption("Chaosy is the best").linkTextReply("Nah.", 2)
    dialogues[0].addOption("I have another question").linkDialogue(dialogues[1])
    dialogues[0].addOption("Quit").makeQuit()

    dialogues[1].addOption("Where am I?").linkTextReply("Life is full of mysteries.", 2)
    dialogues[1].addOption("Help, this system does not work in my map").linkTextReply("Chances are that your editor does not support wurst.", 2)
    dialogues[1].addOption("Back").linkDialogue(dialogues[0])

    // Optional for demo
    setupQuestGiver(dialogues[0])
 
Contents

Dialogue System (Map)

Reviews
MyPad
Your resource has been set to Awaiting Update for the following reasons: Reason: A 7-day period shall be enacted. During that time, if there are any bugs reported within the time period, the author will have the liberty to fix it. If, after 7 days,...
  1. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,165
    Resources:
    3
    Models:
    1
    Icons:
    1
    JASS:
    1
    Resources:
    3

    Nitpicks:



    These points are disputable and are aligned with the user's views:​
    • There are a lot of spaces in the package? Seems kinda counterproductive for coding.
    • @configigurable should be @configurable, though they are annotations and can easily be fixed.
    • What about the capability to go left?

    Notes:


    • trigger.regKeyForPlayers should loop over all players. Ideally, the bj_MAX_PLAYERS constant is used over the literal 12.
    As stated in the nitpicks section, there are a lot of gaps in the package. Otherwise, the package seems well enough on its own.
    Quite nice: 3.5 / 5
     
    Last edited: Apr 25, 2018
  2. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,501
    Resources:
    17
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    17
    1. I cannot copy from visual studio code for some reason, I need to copy it into word first. It fucks up the indenting a bit, but it is better than 0 indenting. @Ralle plz fix
    2. Wops.
    3. I'll look into it
    4. Currently you dedicate a option in order to go back, I did not consider using the left key for that.. note taken. I will probably include it whenever I decide to update this.
     
  3. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,165
    Resources:
    3
    Models:
    1
    Icons:
    1
    JASS:
    1
    Resources:
    3
    Oh, my Word. Try copying the text to Notepad and then copy the text from Notepad and paste them here.

    As for the system, I really find it a nice concept to delve into. :thumbs_up:
     
  4. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,501
    Resources:
    17
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    17
    There are a lot of cool things which it can be used for.


    For example, crafting menu.
    Could be used as an addon to my recipe system.
    Except both needs to be approved first.
     
  5. Dominic74

    Dominic74

    Joined:
    Jul 17, 2010
    Messages:
    139
    Resources:
    0
    Resources:
    0
    the idea of this system is pretty neat, will check it out soon enough.
     
  6. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,501
    Resources:
    17
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    17
    The left arrow key can now be used to go backwards.
    Should not work with up to 24 players
    fixed typo
     
  7. Naze

    Naze

    Arena Moderator

    Joined:
    Nov 12, 2007
    Messages:
    2,265
    Resources:
    6
    Icons:
    4
    Skins:
    1
    Maps:
    1
    Resources:
    6
    This is really cool. I noticed two things though:
    • If the user press Esc, it clears the all the messages and he does not see what's written/what option he is selecting.
    • After pressing right arrow key, the knight portrait disappears, at least until I do another action again.
    I found this upload really nice nonetheless
     
    Last edited: Apr 25, 2018
  8. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,501
    Resources:
    17
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    17
    I fixed the esc problem just now.
    I just need to play around with the black portrait, will see how it goes.

    edit:

    Both issues should now be fixed. Uploading~
     
    Last edited: Apr 25, 2018
  9. LoquendoCrack

    LoquendoCrack

    Joined:
    Jan 11, 2018
    Messages:
    2
    Resources:
    0
    Resources:
    0
    hola, que tal... olle muy cool eso que hiciste
     
  10. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,165
    Resources:
    3
    Models:
    1
    Icons:
    1
    JASS:
    1
    Resources:
    3
    Your resource has been set to Awaiting Update for the following reasons:

    Reason:

    • A 7-day period shall be enacted. During that time, if there are any bugs reported within the time period, the author will have the liberty to fix it.
    • If, after 7 days, the resource is found to be bug-free, or at least good enough to work, it shall be approved.
    • However, if the condition above is not satisfied, the resource shall remain in pending until it satisfies the condition or 6 months have elapsed.
    • After 6 months have elapsed, if the resource is still in Awaiting Update, the moderator may have the liberty to place it under Substandard.
     
  11. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,165
    Resources:
    3
    Models:
    1
    Icons:
    1
    JASS:
    1
    Resources:
    3
    @Cokemonkey11

    What do you think of this resource? Does this adhere to the standards set by Wurst?
     
  12. Cokemonkey11

    Cokemonkey11

    Wurst Reviewer

    Joined:
    May 9, 2006
    Messages:
    3,153
    Resources:
    18
    Tools:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    2
    JASS:
    7
    Resources:
    18
    This is a comment about the API only, as demonstrated in the
    demo
    :

    - API looks great overall, but how do I add arbitrary closure effects to a dialogue option, or chain multiple messages to one dialogue option? I'm thinking something like

    Code (WurstScript):

    dialogues[1].addOption("Where am I?").linkTextReply("Life is full of mysteries.", 2)
        ..andThen("I suppose if you wanted to investigate, you could try exploring the north...")
        ..andThen() d ->
            enableExploreNorthQuest(d.owner)
            disableWhereAmIDialogue()
            doAfter(1.) () ->
                playSound(Sound.hint, d.initiator.getPos())
                print("|cffffcc00Hint:|r you've enabled a quest. Try communicating with other NPCs to discover more quests.")
     
    - What about the name Dialogue? Not a big fan - jass has
    dialog
    . Maybe better, "Conversation"?

    - I care less in the case of the script code, but for demo API I think you should use high-level standard library tools. Can you change your `Table` to a `HashMap`?
     
  13. Cokemonkey11

    Cokemonkey11

    Wurst Reviewer

    Joined:
    May 9, 2006
    Messages:
    3,153
    Resources:
    18
    Tools:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    2
    JASS:
    7
    Resources:
    18
    @Frotty might have some thoughts too
     
  14. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,501
    Resources:
    17
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    17
    Yeah I realized that some sort of callback functionality was needed.

    My quick-fix was to add a callback to each option.
    dialog.addOption("Cool message").makeQuit().callback(function onOptionSelect)

    all option functions (on my local version) returns the instance meaning you can chain the functions together. I think it gives better readability than mashing everything into the constructor.

    And in the callback you can use

    public function getTriggeredOption() returns Option
    public function getTriggeringOptionPlayer() returns player

    having access to the Option instance gives access to most of the relevant information
     
  15. Cokemonkey11

    Cokemonkey11

    Wurst Reviewer

    Joined:
    May 9, 2006
    Messages:
    3,153
    Resources:
    18
    Tools:
    1
    Maps:
    5
    Spells:
    3
    Tutorials:
    2
    JASS:
    7
    Resources:
    18
    I would prefer if the callback closure had context in arguments rather than magic free functions
     
  16. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,501
    Resources:
    17
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    17
    That'd be neat but I got no clue how to make those.
     
  17. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,501
    Resources:
    17
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    17
    Long overdue update.

    • Callbacks added
    • Wrapper functions for getting the unit that started the conversation
    • simplified demo code
     
  18. Titanhex

    Titanhex

    Joined:
    Jul 26, 2008
    Messages:
    1,007
    Resources:
    2
    Maps:
    2
    Resources:
    2
    Seems to kickback an error whenever I target the Blood Mage, though perhaps that's intended.

    I could have many uses for this, and I'm sure anyone with the confidence to use it would find many uses as well.
     
  19. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,501
    Resources:
    17
    Maps:
    1
    Spells:
    10
    Tutorials:
    6
    Resources:
    17
    Yeah. It tries to open the dialogue attached but there is none.

    If you use your own demo code it's fine.