yarn

package module
v0.6.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 18, 2024 License: Apache-2.0 Imports: 23 Imported by: 0

README

yarn

A Go implementation of parts of Yarn Spinner 2.3.

Build status Go Reference Go Report Card License

The yarn package is a Go implementation of the Yarn Spinner 2.0 dialogue system. Given a compiled .yarn file (into the VM bytecode and string table) and DialogueHandler implementation, the VirtualMachine can execute the program as the original Yarn Spinner VM would, delivering lines, options, and commands to the handler.

Supported features

  • ✅ All Yarn Spinner 2.0 machine opcodes, instruction forms, and standard functions.
  • ✅ Custom functions, similar to the text/template package.
  • ✅ Yarn Spinner CSV string tables.
  • ✅ String substitutions (Hello, {0} - you're looking well!).
  • select format function (Hey [select value={0} m="bro" f="sis" nb="doc"]).
  • plural format function (That'll be [plural value={0} one="% dollar" other="% dollars"]).
  • ordinal format function (You are currently [ordinal value={0} one="%st" two="%nd" few="%rd" other="%th"] in the queue).
    • ✅ ...including using Unicode CLDR for cardinal/ordinal form selection (en-AU not assumed!)
  • ✅ Custom markup tags are also parsed, and rendered to an AttributedString.
  • visited and visit_count
  • ✅ Built-in functions like dice, round, and floor that are mentioned in the Yarn Spinner documentation.

Basic Usage

  1. Compile your .yarn file. You can probably get the compiled output from a Unity project, or you can compile without using Unity with a tool like the Yarn Spinner Console:

    ysc compile Example.yarn
    

    This produces two files: the VM bytecode .yarnc, and a string table .csv.

  2. Implement a DialogueHandler, which receives events from the VM. Here's an example that plays the dialogue on the terminal:

    type MyHandler struct{
        stringTable *yarn.StringTable
        // ... and your own fields ...
    }
    
    func (m *MyHandler) Line(line yarn.Line) error {
        // StringTable's Render turns the Line into a string, applying all the
        // substitutions and format functions that might be present.
        text, _ := m.stringTable.Render(line)
        fmt.Println(text)
        // You can block in here to give the player time to read the text.
        fmt.Println("\n\nPress ENTER to continue")
        fmt.Scanln()
        return nil
    }
    
    func (m *MyHandler) Options(opts []yarn.Option) (int, error) {
        fmt.Println("Choose:")
        for _, opt := range opts {
            text, _ := m.stringTable.Render(opt.Line)
            fmt.Printf("%d: %s\n", opt.ID, text)
        }
        fmt.Print("Enter the number of your choice: ")
        var choice int
        fmt.Scanln(&choice)
        return choice, nil
    }
    
    // ... and also the other methods.
    // Alternatively you can embed yarn.FakeDialogueHandler in your handler.
    
  3. Load the two files, your DialogueHandler, a VariableStorage, and any custom functions, into a VirtualMachine, and then pass the name of the first node to Run:

    package main
    
    import "drjosh.dev/yarn"
    
    func main() {
        // Load the files (error handling omitted for brevity):
        program, stringTable, _ := yarn.LoadFiles("Example.yarn.yarnc", "en-AU")
    
        // Set up your DialogueHandler and the VirtualMachine:
        myHandler := &MyHandler{
            stringTable: stringTable,
        }
        vm := &yarn.VirtualMachine{
            Program: program,
            Handler: myHandler,
            Vars:    yarn.NewMapVariableStorage(), // or your own VariableStorage implementation
            FuncMap: yarn.FuncMap{ // this is optional
                "last_value": func(x ...any) any {
                    return x[len(x)-1]
                },
                // or your own custom functions!
            }
        }
    
        // Run the VirtualMachine starting with the Start node!
        vm.Run("Start")
    }
    

See cmd/yarnrunner.go for a complete example.

Async usage

To avoid the VM delivering the lines, options, and commands all at once, your DialogueHandler implementation is allowed to block execution of the VM goroutine - for example, using a channel operation.

However, in a typical game, each line or option would be associated with two distinct operations: showing the line/option to the player, and hiding it later on in response to user input.

To make this easier, AsyncAdapter can handle blocking the VM for you.

sequenceDiagram
  yarn.VirtualMachine->>+yarn.AsyncAdapter: Line
  yarn.AsyncAdapter->>+myHandler: Line
  myHandler->>-gameEngine: showDialogue
  Note right of myHandler: (time passes)
  gameEngine->>+myHandler: Update
  myHandler->>gameEngine: hideDialogue
  myHandler->>-yarn.AsyncAdapter: Go
  yarn.AsyncAdapter-->>-yarn.VirtualMachine: (return)

Use AsyncAdapter as the VirtualMachine.Handler, and create the AsyncAdapter with an AsyncDialogueHandler:

// MyHandler should now implement yarn.AsyncDialogueHandler.
type MyHandler struct {
    stringTable *yarn.StringTable

    dialogueDisplay Component

    // Maintain a reference to the AsyncAdapter in order to call Go on it
    // in response to user input.
    // (It doesn't have to be stored in the handler, there are probably better
    // places in a real project. This is just an example.)
    asyncAdapter *yarn.AsyncAdapter
}

// Line is called by AsyncAdapter from the goroutine running VirtualMachine.Run.
// The AsyncAdapter pauses the VM.
func (m *MyHandler) Line(line yarn.Line) {
    text, _ := m.stringTable.Render(line)
    m.dialogueDisplay.Show(text)
}

// Update is called on every tick by the game engine, which is a separate
// goroutine to the one the Yarn virtual machine is running in.
func (m *MyHandler) Update() error {
    //...

    if m.dialogueDisplay.Visible() && inpututil.IsKeyJustPressed(ebiten.KeyEnter) {
        // Hide the dialogue display.
        m.dialogueDisplay.Hide()

        // Calling AsyncAdapter.Go un-pauses the VM.
        m.asyncAdapter.Go()
    }
    //...
}

// --- Setup ---

myHandler := &MyHandler{}
myHandler.asyncAdapter = yarn.NewAsyncAdapter(myHandler)

vm := &yarn.VirtualMachine{
    Program: program,
    Handler: myHandler.asyncAdapter,
    ...
}

Usage notes

Note that using an earlier Yarn Spinner compiler will result in some unusual behaviour when compiling Yarn files with newer features. For example, with v1.0 <<jump ...>> may be compiled as a command. Your implementation of Command may implement jump by calling the SetNode VM method.

If you need the tags for a node, you can read these from the Node protobuf message directly. Source text of a rawText node can be looked up manually:

prog, st, _ := yarn.LoadFiles("testdata/Example.yarn.yarnc", "en")
node := prog.Nodes["LearnMore"]
// Tags for the LearnMore node:
fmt.Println(node.Tags)
// Source text string ID:
fmt.Println(node.SourceTextStringID)
// Source text is in the string table:
fmt.Println(st.Table[node.SourceTextStringID].Text)

Licence

This project is available under the Apache 2.0 license. See the LICENSE file for more information.

The bytecode and testdata directories contains files or derivative works from Yarn Spinner. See bytecode/README.md and testdata/README.md for more information.

Documentation

Overview

Package yarn implements the Yarn Spinner virtual machine and dialogue system. For the original implementation, see https://yarnspinner.dev and https://github.com/YarnSpinnerTool/YarnSpinner.

Index

Constants

View Source
const (
	// No event has been delivered (since the last call to Go / GoWithOption);
	// the VM is executing.
	VMStateRunning = iota

	// An event other than Options was delivered, and VM execution is blocked.
	VMStatePaused

	// Options event was delivered, and VM execution is blocked.
	VMStatePausedOptions

	// Execution has not begun, or has ended (e.g. by calling Abort, or any
	// other error).
	VMStateStopped
)
View Source
const (
	// ErrNilDialogueHandler indicates that Handler hasn't been set.
	ErrNilDialogueHandler = virtualMachineError("nil dialogue handler")

	// ErrNilVariableStorage indicates that Vars hasn't been set.
	ErrNilVariableStorage = virtualMachineError("nil variable storage")

	// ErrMissingProgram indicates that Program hasn't been set.
	ErrMissingProgram = virtualMachineError("missing or empty program")

	// ErrNoOptions indicates the program is invalid - it tried to show options
	// but none had been added.
	ErrNoOptions = virtualMachineError("no options were added")

	// ErrStackUnderflow indicates the program tried to pop or peek when the
	// stack was empty.
	ErrStackUnderflow = virtualMachineError("stack underflow")

	// ErrWrongType indicates the program needed a stack value, operand, or
	// function of one type, but got something else instead.
	ErrWrongType = virtualMachineError("wrong type")

	// ErrNotConvertible indicates the program tried to convert a stack value
	// or operand to a different type, but it was not convertible to that type.
	ErrNotConvertible = virtualMachineError("not convertible")

	// ErrNodeNotFound is returned where Run or SetNode is passed the name of a
	// node that is not in the program.
	ErrNodeNotFound = virtualMachineError("node not found")

	// ErrLabelNotFound indicates the program tries to jump to a label that
	// isn't in the label table for the current node.
	ErrLabelNotFound = virtualMachineError("label not found")

	// ErrNilOperand indicates the a malformed program containing an instruction
	// that requires a usable operand but the operand was nil.
	ErrNilOperand = virtualMachineError("nil operand")

	// ErrFunctionNotFound indicates the program tried to call a function but
	// that function is not in the FuncMap.
	ErrFunctionNotFound = virtualMachineError("function not found")

	// ErrFunctionArgMismatch indicates the program tried to call a function but
	// had the wrong number or types of args to pass to it.
	ErrFunctionArgMismatch = virtualMachineError("arg mismatch")
)

Various sentinel errors returned by the virtual machine.

View Source
const ErrAlreadyStopped = virtualMachineError("VM already stopped or stopping")

ErrAlreadyStopped is returned when the AsyncAdapter cannot stop the virtual machine, because it is already stopped.

View Source
const Stop = virtualMachineError("stop")

Stop stops the virtual machine without error. It is used by the STOP instruction, but can also be returned by your handler to stop the VM in the same way. However a stop happens, NodeComplete and DialogueComplete are still called.

Variables

This section is empty.

Functions

func ConvertToBool

func ConvertToBool(x interface{}) (bool, error)

ConvertToBool attempts conversion of the standard Yarn Spinner VM types (bool, number, string, null) to bool.

func ConvertToFloat32

func ConvertToFloat32(x interface{}) (float32, error)

ConvertToFloat32 attempts conversion of the standard Yarn Spinner VM types (bool, number, string, null) to a float32.

func ConvertToFloat64

func ConvertToFloat64(x interface{}) (float64, error)

ConvertToFloat64 attempts conversion of the standard Yarn Spinner VM types (bool, number, string, null) to a float64.

func ConvertToInt

func ConvertToInt(x interface{}) (int, error)

ConvertToInt attempts conversion of the standard Yarn Spinner VM types to (bool, number, string, null) to int.

func ConvertToString

func ConvertToString(x interface{}) string

ConvertToString converts a value to a string, in a way that matches what Yarn Spinner does. nil becomes "null", and booleans are title-cased.

func FormatInstruction

func FormatInstruction(inst *yarnpb.Instruction) string

FormatInstruction prints an instruction in a format convenient for debugging. The output is intended for human consumption only and may change between incremental versions of this package.

func FormatProgram

func FormatProgram(w io.Writer, prog *yarnpb.Program) error

FormatProgram prints a program in a format convenient for debugging to the io.Writer. The output is intended for human consumption only and may change between incremental versions of this package.

func FormatProgramString

func FormatProgramString(prog *yarnpb.Program) string

FormatProgramString prints the whole program into a string.

func LoadProgramFile

func LoadProgramFile(programPath string) (*yarnpb.Program, error)

LoadProgramFile is a convenient function for loading a compiled Yarn Spinner program given a file path.

Types

type AsyncAdapter

type AsyncAdapter struct {
	// contains filtered or unexported fields
}

AsyncAdapter is a DialogueHandler that exposes an interface that is similar to the mainline YarnSpinner VM dialogue handler. Instead of manually blocking inside the DialogueHandler callbacks, AsyncAdapter does this for you, until you call Go, GoWithChoice, or Abort (as appropriate).

func NewAsyncAdapter

func NewAsyncAdapter(h AsyncDialogueHandler) *AsyncAdapter

NewAsyncAdapter returns a new AsyncAdapter.

func (*AsyncAdapter) Abort

func (a *AsyncAdapter) Abort(err error) error

Abort stops the VM with the given error as soon as possible (either within the current event, or on the next event). If a nil error is passed, Abort will replace it with Stop (so that NodeComplete and DialogueComplete still fire). If the VM is already stopped (either through Abort, or after the DialogueComplete event) an error will be returned.

func (*AsyncAdapter) Command

func (a *AsyncAdapter) Command(command string) error

Command is called by the VM and blocks until Go or Abort is called.

func (*AsyncAdapter) DialogueComplete

func (a *AsyncAdapter) DialogueComplete() error

DialogueComplete is called by the VM and blocks until Go or Abort is called.

func (*AsyncAdapter) Go

func (a *AsyncAdapter) Go() error

Go will continue the VM after it has delivered any event (other than Options). If the VM is not paused following any event other than Options, an error will be returned.

func (*AsyncAdapter) GoWithChoice

func (a *AsyncAdapter) GoWithChoice(id int) error

GoWithChoice will continue the VM after it has delivered an Options event. Pass the ID of the chosen option. If the VM is not paused following an Options event, an error will be returned.

func (*AsyncAdapter) Line

func (a *AsyncAdapter) Line(line Line) error

Line is called by the VM and blocks until Go or Abort is called.

func (*AsyncAdapter) NodeComplete

func (a *AsyncAdapter) NodeComplete(nodeName string) error

NodeComplete is called by the VM and blocks until Go or Abort is called.

func (*AsyncAdapter) NodeStart

func (a *AsyncAdapter) NodeStart(nodeName string) error

NodeStart is called by the VM and blocks until Go or Abort is called.

func (*AsyncAdapter) Options

func (a *AsyncAdapter) Options(options []Option) (int, error)

Options is called by the VM and blocks until GoWithChoice or Abort is called.

func (*AsyncAdapter) PrepareForLines

func (a *AsyncAdapter) PrepareForLines(lineIDs []string) error

PrepareForLines is called by the VM and blocks until Go or Abort is called.

func (*AsyncAdapter) State

func (a *AsyncAdapter) State() VMState

State returns the current state.

type AsyncDialogueHandler

type AsyncDialogueHandler interface {
	// NodeStart is called when a node has begun executing. It is passed the
	// name of the node.
	NodeStart(nodeName string)

	// PrepareForLines is called when the dialogue system anticipates that it
	// will deliver some lines. Note that not every line prepared may end up
	// being run.
	PrepareForLines(lineIDs []string)

	// Line is called when the dialogue system runs a line of dialogue.
	Line(line Line)

	// Options is called to deliver a set of options to the game. The player
	// should choose one of the options.
	Options(options []Option)

	// Command is called when the dialogue system runs a command.
	Command(command string)

	// NodeComplete is called when a node has completed execution. It is passed
	// the name of the node.
	NodeComplete(nodeName string)

	// DialogueComplete is called when the dialogue as a whole is complete.
	DialogueComplete()
}

AsyncDialogueHandler receives events from AsyncAdapter. Unlike DialogueHandler, during each event the VM execution is paused automatically until Go, GoWithChoice, or Abort is called.

type Attribute

type Attribute struct {
	Start, End int
	Name       string
	Props      map[string]string
}

Attribute describes a range within a string with additional information provided by markup tags. Start and End specify the range in bytes. Name is the tag name, and Props contains any additional key="value" tag properties.

type AttributedString

type AttributedString struct {
	// contains filtered or unexported fields
}

AttributedString is a string with additional attributes, such as presentation or styling information, that apply to the whole string or substrings.

func (*AttributedString) ScanAttribEvents

func (s *AttributedString) ScanAttribEvents(visit func(pos int, atts []*Attribute))

ScanAttribEvents calls visit with each change in attribute state. pos is the byte position in the string where the change occurs. atts will contain the attributes that either start or end at pos, in the same order they were read from the original markup. Self-closing tags, or an open and close pair that apply to the same position (i.e. marking up nothing) will only be present in atts once (in the order of the start tag). For example, for the original string:

`[a]Hello A[/a] [b]Hello B[/b] [c][d][/c]No C, [e/]only D[/d]`

which is processed into the unattributed string:

`Hello A Hello B No C, only D`

ScanAttribEvents will visit: * (0, [a]) -- open of a * (7, [a]) -- close of a * (8, [b]) -- open of b * (15, [b]) -- close of b * (16, [c,d]) -- close of c applies to same position, so it appears once * (22, [e]) -- e is self-closing, so it appears once * (28, [d]) -- close of d

func (*AttributedString) String

func (s *AttributedString) String() string

type DialogueHandler

type DialogueHandler interface {
	// NodeStart is called when a node has begun executing. It is passed the
	// name of the node.
	NodeStart(nodeName string) error

	// PrepareForLines is called when the dialogue system anticipates that it
	// will deliver some lines. Note that not every line prepared may end up
	// being run.
	PrepareForLines(lineIDs []string) error

	// Line is called when the dialogue system runs a line of dialogue.
	Line(line Line) error

	// Options is called to deliver a set of options to the game. The player
	// should choose one of the options, and Options should return the ID of the
	// chosen option.
	Options(options []Option) (int, error)

	// Command is called when the dialogue system runs a command.
	Command(command string) error

	// NodeComplete is called when a node has completed execution. It is passed
	// the name of the node.
	NodeComplete(nodeName string) error

	// DialogueComplete is called when the dialogue as a whole is complete.
	DialogueComplete() error
}

DialogueHandler receives events from the virtual machine.

type FakeAsyncDialogueHandler

type FakeAsyncDialogueHandler struct {
	AsyncAdapter *AsyncAdapter
}

FakeAsyncDialogueHandler implements AsyncDialogueHandler with minimal methods that immediately continue the VM. This is useful both for testing, and for satisfying the AsyncDialogueHandler interface via embedding, e.g.:

	   type MyHandler struct {
		      FakeAsyncDialogueHandler
	   }
	   // MyHandler is only interested in Line and Options.
	   func (m MyHandler) Line(line Line) { ... }
	   func (m MyHandler) Options(options []Option) { ... }
	   // All the other AsyncDialogueHandler methods provided by
       // FakeAsyncDialogueHandler.

Note that FakeAsyncDialogueHandler needs a reference to the AsyncAdapter (in order to call Go or GoWithChoice).

func (FakeAsyncDialogueHandler) Command

func (f FakeAsyncDialogueHandler) Command(string)

Command calls AsyncAdapter.Go.

func (FakeAsyncDialogueHandler) DialogueComplete

func (f FakeAsyncDialogueHandler) DialogueComplete()

DialogueComplete calls AsyncAdapter.Go.

func (FakeAsyncDialogueHandler) Line

Line calls AsyncAdapter.Go.

func (FakeAsyncDialogueHandler) NodeComplete

func (f FakeAsyncDialogueHandler) NodeComplete(string)

NodeComplete calls AsyncAdapter.Go.

func (FakeAsyncDialogueHandler) NodeStart

func (f FakeAsyncDialogueHandler) NodeStart(string)

NodeStart calls AsyncAdapter.Go.

func (FakeAsyncDialogueHandler) Options

func (f FakeAsyncDialogueHandler) Options(options []Option)

Options calls AsyncAdapter.GoWithChoice with the ID of the first option, or AsyncAdapter.Abort if there are no options.

func (FakeAsyncDialogueHandler) PrepareForLines

func (f FakeAsyncDialogueHandler) PrepareForLines([]string)

PrepareForLines calls AsyncAdapter.Go.

type FakeDialogueHandler

type FakeDialogueHandler struct{}

FakeDialogueHandler implements DialogueHandler with minimal, do-nothing methods. This is useful both for testing, and for satisfying the DialogueHandler via embedding, e.g.:

   type MyHandler struct {
	      FakeDialogueHandler
   }
   // MyHandler is only interested in Line and Options.
   func (m MyHandler) Line(line Line) error { ... }
   func (m MyHandler) Options(options []Option) (int, error) { ... }
   // All the other DialogueHandler methods provided by FakeDialogueHandler.

func (FakeDialogueHandler) Command

func (FakeDialogueHandler) Command(string) error

Command returns nil.

func (FakeDialogueHandler) DialogueComplete

func (FakeDialogueHandler) DialogueComplete() error

DialogueComplete returns nil.

func (FakeDialogueHandler) Line

Line returns nil.

func (FakeDialogueHandler) NodeComplete

func (FakeDialogueHandler) NodeComplete(string) error

NodeComplete returns nil.

func (FakeDialogueHandler) NodeStart

func (FakeDialogueHandler) NodeStart(string) error

NodeStart returns nil.

func (FakeDialogueHandler) Options

func (FakeDialogueHandler) Options(options []Option) (int, error)

Options returns the first option ID, or an error if there are no options.

func (FakeDialogueHandler) PrepareForLines

func (FakeDialogueHandler) PrepareForLines([]string) error

PrepareForLines returns nil.

type FuncMap

type FuncMap map[string]interface{}

FuncMap maps function names to implementations. It is similar to the text/template FuncMap.

Each function must return either 0, 1, or 2 values, and if 2 are returned, the latter must be type `error`.

If the arguments being passed by the program are not assignable to an argument, and the argument has type bool, int, float32, float64, or string, then a conversion is attempted by the VM. For example, if the stack has the values ("3", true, 2) on top, CALL_FUNC with "Number.Add" (see below) would cause Number.Add's implementation to be called with (3.0, 1.0) (the 2 is the argument count).

type Line

type Line struct {
	// The string ID for the line.
	ID string
	// Values that should be interpolated into the user-facing text.
	Substitutions []string
}

Line represents a line of dialogue.

type MapVariableStorage

type MapVariableStorage struct {
	// contains filtered or unexported fields
}

MapVariableStorage implements VariableStorage, in memory, using a map. In addition to the core VariableStorage functionality, there are methods for accessing the contents as an ordinary map[string]any.

func NewMapVariableStorage

func NewMapVariableStorage() *MapVariableStorage

NewMapVariableStorage creates a new empty MapVariableStorage.

func NewMapVariableStorageFromMap

func NewMapVariableStorageFromMap(src map[string]any) *MapVariableStorage

NewMapVariableStorageFromMap creates a new MapVariableStorage with initial contents copied from src. It does not keep a reference to src.

func (*MapVariableStorage) Clear

func (m *MapVariableStorage) Clear()

Clear empties the storage of all values.

func (*MapVariableStorage) Clone

Clone returns a new MapVariableStorage that is a clone of the receiver. The new storage is a deep copy, and does not contain a reference to the original map inside the receiver (to avoid accidental data races).

func (*MapVariableStorage) Contents

func (m *MapVariableStorage) Contents() map[string]any

Contents returns a copy of the contents of the storage, as a regular map. The returned map is a copy, it is not a reference to the map contained within the storage (to avoid accidental data races).

func (*MapVariableStorage) Delete

func (m *MapVariableStorage) Delete(names ...string)

Delete deletes values from the storage.

func (*MapVariableStorage) GetValue

func (m *MapVariableStorage) GetValue(name string) (value any, found bool)

GetValue fetches a value from the storage, returning (nil, false) if not present.

func (*MapVariableStorage) ReplaceContents

func (m *MapVariableStorage) ReplaceContents(src map[string]any)

ReplaceContents replaces the contents of the storage with values from a regular map. ReplaceContents copies src, it does not keep a reference to src (to avoid accidental data races).

func (*MapVariableStorage) SetValue

func (m *MapVariableStorage) SetValue(name string, value any)

SetValue sets a value in the storage.

type Option

type Option struct {
	// A number identifying this option. If this option is selected, pass
	// this number back to the dialogue system.
	ID int

	// The line that should be presented for this option.
	Line Line

	// Name of the node that will run next, if this option is selected.
	DestinationNode string

	// Indicates whether the player should be permitted to select the option.
	// This is false for options that the player _could_ have taken if they had
	// satisfied some prerequisite earlier in the game.
	IsAvailable bool
}

Option represents one option (among others) that the player could choose.

type StringTable

type StringTable struct {
	Language language.Tag
	Table    map[string]*StringTableRow
}

StringTable contains all the information from a string table, keyed by string ID. This can be constructed either by using ReadStringTable, or manually (e.g. if you are not using Yarn Spinner CSV string tables but still want to use substitutions, format functions, and markup tags).

func LoadFiles

func LoadFiles(programPath, langCode string) (*yarnpb.Program, *StringTable, error)

LoadFiles is a convenient way of loading a compiled Yarn Spinner program and string table from files in one function call. When passing a programPath named foo/bar/file.yarnc, LoadFiles expects that files named foo/bar/file-Lines.csv and foo/bar/file-Metadata.csv are also available. langCode should be a valid BCP 47 language tag.

func LoadFilesFS

func LoadFilesFS(fsys fs.FS, programPath, langCode string) (*yarnpb.Program, *StringTable, error)

LoadFilesFS loads compiled Yarn Spinner files from the provided fs.FS. See LoadFiles for more information.

func LoadStringTableFile

func LoadStringTableFile(stringTablePath, langCode string) (*StringTable, error)

LoadStringTableFile is a convenient function for loading a CSV string table given a file path. If stringTablePath is foo/bar/file-Lines.csv then it expects a corresponding Metadata file at foo/bar/file-Metadata.csv. It assumes the first row of both files are a header. langCode must be a valid BCP 47 language tag.

func LoadStringTableFileFS

func LoadStringTableFileFS(fsys fs.FS, stringTablePath, langCode string) (*StringTable, error)

LoadStringTableFileFS loads compiled Yarn Spinner files from the provided fs.FS. See LoadStringTableFile for details.

func ReadStringTable

func ReadStringTable(r io.Reader, langCode string) (*StringTable, error)

ReadStringTable reads a CSV string table from the reader. It assumes the first row is a header. langCode must be a valid BCP 47 language tag. In addition to checking the CSV structure as it is parsed, each lineNumber is parsed as an int, and each text is also parsed. Any malformed substitution tokens or markup tags will cause an error.

func (*StringTable) Render

func (t *StringTable) Render(line Line) (*AttributedString, error)

Render looks up the row corresponding to line.ID, interpolates substitutions (from line.Substitutions), applies format functions, and processes style tags into attributes.

type StringTableRow

type StringTableRow struct {
	ID, Text, File, Node string
	LineNumber           int

	Tags []string // Tags are set in the metadata table.
	// contains filtered or unexported fields
}

StringTableRow contains all the information from one row in a string table.

func (*StringTableRow) Render

func (r *StringTableRow) Render(substs []string, lang language.Tag) (*AttributedString, error)

Render interpolates substitutions, applies format functions, and processes style tags into attributes.

type TestPlan

type TestPlan struct {
	StringTable *StringTable
	Steps       []TestStep
	Step        int

	FakeDialogueHandler // implements remaining methods
	// contains filtered or unexported fields
}

TestPlan implements test plans. A test plan is a dialogue handler that expects specific lines and options from the dialogue system.

func LoadTestPlanFile

func LoadTestPlanFile(testPlanPath string) (*TestPlan, error)

LoadTestPlanFile is a convenient function for loading a test plan given a file path.

func ReadTestPlan

func ReadTestPlan(r io.Reader) (*TestPlan, error)

ReadTestPlan reads a testplan from an io.Reader into a TestPlan.

func (*TestPlan) Command

func (p *TestPlan) Command(command string) error

Command handles the command... somehow.

func (*TestPlan) Complete

func (p *TestPlan) Complete() error

Complete checks if the test plan was completed.

func (*TestPlan) DialogueComplete

func (p *TestPlan) DialogueComplete() error

DialogueComplete records the event in p.DialogueCompleted.

func (*TestPlan) Line

func (p *TestPlan) Line(line Line) error

Line checks that the line matches the one expected by the plan.

func (*TestPlan) Options

func (p *TestPlan) Options(opts []Option) (int, error)

Options checks that the options match those expected by the plan, then selects the option specified in the plan.

type TestStep

type TestStep struct {
	Type     string
	Contents string
}

TestStep is a step in a test plan.

func (TestStep) String

func (s TestStep) String() string

type VMState

type VMState int32

VMState enumerates the different states that AsyncAdapter can be in.

func (VMState) String

func (s VMState) String() string

type VMStateMismatchErr

type VMStateMismatchErr struct {
	// The VM was in state Got, but we wanted it to be in state Want in order
	// to change it to state Next.
	Got, Want, Next VMState
}

VMStateMismatchErr is returned when AsyncAdapter is told to do something (either by the user calling Go, GoWithChoice, or Abort, or the VM calling a DialogueHandler method) but this requires AsyncAdapter to be in a different state than the state it is in.

func (VMStateMismatchErr) Error

func (e VMStateMismatchErr) Error() string

type VariableStorage

type VariableStorage interface {
	GetValue(name string) (value any, ok bool)
	SetValue(name string, value any)
}

VariableStorage stores values of any kind.

type VirtualMachine

type VirtualMachine struct {
	// Program is the program to execute.
	Program *yarnpb.Program

	// Handler receives content (lines, options, etc) and other events.
	Handler DialogueHandler

	// Vars stores variables used and provided by the dialogue.
	Vars VariableStorage

	// FuncMap is used to provide user-defined functions.
	FuncMap FuncMap

	// TraceLogf, if not nil, is called before each instruction to log the
	// current stack, options, and the instruction about to be executed.
	TraceLogf func(string, ...interface{})
	// contains filtered or unexported fields
}

VirtualMachine implements the Yarn Spinner virtual machine.

func (*VirtualMachine) Run

func (vm *VirtualMachine) Run(startNode string) error

Run executes the program, starting at a particular node.

func (*VirtualMachine) SetNode

func (vm *VirtualMachine) SetNode(name string) error

SetNode sets the VM to begin a node. If a node is already selected, NodeComplete will be called for that node. Then NodeStart and PrepareForLines will be called (for the newly selected node). Passing the current node is one way to reset to the start of the node.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL