Skip to content

Instantly share code, notes, and snippets.

@thetooth
Created October 7, 2018 06:34
Show Gist options
  • Save thetooth/0a5fdaea13f0006cdb7886c0ccd3def6 to your computer and use it in GitHub Desktop.
Save thetooth/0a5fdaea13f0006cdb7886c0ccd3def6 to your computer and use it in GitHub Desktop.
package dialog
import (
"encoding/json"
"fmt"
"io/ioutil"
"github.com/gen2brain/raylib-go/raylib"
"github.com/sirupsen/logrus"
)
// NewConversation returns a dialog tree
func NewConversation(fileName string) *Conversation {
c := &Conversation{
Nodes: make(map[string]*Node),
Variables: make(map[string]string),
}
b, err := ioutil.ReadFile(fileName)
if err != nil {
logrus.Error(err)
}
nodes := []*Node{}
if err := json.Unmarshal(b, &nodes); err != nil {
logrus.Error(err)
}
for _, node := range nodes {
c.Nodes[node.ID] = node
}
logrus.Info("Loaded ", len(c.Nodes), " nodes")
return c
}
// Node stores each tree node and it's possible values
type Node struct {
ID string
Type string
Actor string
Name string
Next string
Branches map[string]string
Choices []string
Variable string
Value string
}
// Conversation stores the dialog tree, branch variables and currently active node for immediate mode
type Conversation struct {
Nodes map[string]*Node
Variables map[string]string
CurrentNode *Node
}
// Next advances through the tree until either a Text or Choice node is encountered.
// We return on these two types for drawing and interaction.
func (c *Conversation) Next(node *Node) *Node {
logrus.Info("Execute ", node.Type, " ", node.Actor)
c.CurrentNode = node
next := ""
switch node.Type {
case "Node":
next = node.Next
case "Text":
return node
case "Choice":
return node
case "Branch":
if v, ok := c.Variables[node.Variable]; ok {
if nextBranch, ok := node.Branches[v]; ok {
next = nextBranch
} else {
next = node.Branches["_default"]
}
} else {
logrus.Warn("Variable '", node.Variable, "' not present, will default")
next = node.Branches["_default"]
}
case "Set":
c.Set(node.Variable, node.Value)
next = node.Next
}
if next != "" {
return c.Next(c.Nodes[next])
}
return nil
}
// Talk finds the start of dialog for a given actor returning the first text node.
func (c *Conversation) Talk(actor string) *Node {
for _, node := range c.Nodes {
if node.Type == "Node" && node.Actor == actor && node.Name == "start" {
c.CurrentNode = node
return c.Next(node)
}
}
return nil
}
// Bye clears the current node so it won't be drawn.
func (c *Conversation) Bye() {
c.CurrentNode = nil
}
// Choose an option or just advance the text
func (c *Conversation) Choose(choice int) {
if c.CurrentNode == nil {
return
}
if c.CurrentNode.Type == "Text" {
choices := c.CurrentNode.Choices
if len(choices) > 0 {
c.Next(c.Nodes[choices[choice]])
} else if c.CurrentNode.Next != "" {
c.Next(c.Nodes[c.CurrentNode.Next])
} else {
c.Bye()
}
} else if c.CurrentNode.Type == "Choice" {
c.Next(c.Nodes[c.CurrentNode.Next])
}
}
// Set variable to value (is this even needed?)
func (c *Conversation) Set(variable, value string) {
logrus.Info(variable, " = ", value)
c.Variables[variable] = value
}
// Draw currently active dialog
func (c *Conversation) Draw(x, y int32) {
if c.CurrentNode == nil {
return
}
text := ""
if c.CurrentNode.Type == "Choice" {
text += fmt.Sprintf("(You):\n\n%v", c.CurrentNode.Name)
} else {
text += fmt.Sprintf("%v:\n\n%v\n", c.CurrentNode.Actor, c.CurrentNode.Name)
for _, choice := range c.CurrentNode.Choices {
text += fmt.Sprintf("[ %v ] ", c.Nodes[choice].Name)
}
}
raylib.DrawText(text, x, y, 8, raylib.White)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment