1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  4. Travel to distant realms and encounter scenes unknown to the common folk. The Greatest of Adventures is upon us with the 8th Cinematic Contest. Join in on a fun ride.
    Dismiss Notice
  5. The 18th Icon Contest is ON! Choose any ingame unit and give him/her Hero abilities. Good luck to all.
    Dismiss Notice
  6. The Secrets of Warcraft 3 have revealed interesting works. The RESULTS for Abelhawk's Mini-Mapping Contest #15 have come out!
    Dismiss Notice
  7. Contestants are to create a scene set in the Stone Age. Come and see what you can come up with. We wish you the best of luck!
    Dismiss Notice
  8. Colour outside the lines! Techtree Contest #13 is a go. The contest is optionally paired.
    Dismiss Notice
  9. Night Rider gained several songs for his journey. The poll for the 12th Music Contest has started. Check it out!
    Dismiss Notice
  10. Greetings cerebrates, our Swarm needs new spawners that will have numerous children. Join the HIVE's 31st Modeling Contest - Spawners and Spawned! The contest is optionally paired.
    Dismiss Notice
  11. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Dialogue System

Submitted by Chaosy
This bundle is marked as approved. It works and satisfies the submission rules.
[​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
This has been overlooked for quite some time. Approved
  1. MyPad

    MyPad

    Spell Reviewer

    Joined:
    May 9, 2014
    Messages:
    1,270
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6

    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,601
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    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,270
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    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,601
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    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,601
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    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,286
    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,601
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    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,270
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    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,270
    Resources:
    6
    Models:
    1
    Icons:
    1
    Spells:
    3
    JASS:
    1
    Resources:
    6
    @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,238
    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,238
    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,601
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    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,238
    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,601
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    That'd be neat but I got no clue how to make those.
     
  17. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    10,601
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    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,601
    Resources:
    18
    Maps:
    1
    Spells:
    11
    Tutorials:
    6
    Resources:
    18
    Yeah. It tries to open the dialogue attached but there is none.

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