gouache

package module
v0.0.0-...-29e0aad Latest Latest
Warning

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

Go to latest
Published: Mar 27, 2025 License: MIT Imports: 12 Imported by: 0

README

gouache

Go runtime for Ink narrative scripting language.

This is very much a work in progress and missing many features.

It's intended to be used as a library for embedding Ink dialogs in Go.

Documentation

Index

Constants

View Source
const (
	MinInkVersion = 19
	MaxInkVersion = 21
)

Variables

View Source
var ErrUnsupportedVersion = fmt.Errorf("unsupported version")

Functions

func Init

func Init(c Container, listDefs ListDefs) (Element, Evaluator)

func LoadJSON

func LoadJSON(r io.Reader) (Container, ListDefs, error)

Types

type Address

type Address string

func (Address) Contains

func (a Address) Contains(b Address) bool

func (Address) Parent

func (a Address) Parent() Address

type BaseEvaluator

type BaseEvaluator struct {
}

func (BaseEvaluator) Step

func (e BaseEvaluator) Step(stack *CallFrame, el Element) (Output, *Choice, Element, *CallFrame, Stepper)

type BeginEval

type BeginEval struct{} // "ev"

type BeginStringEval

type BeginStringEval struct{} // "str"

type BeginTag

type BeginTag struct{} // "#"

type BinOp

type BinOp func(a, b Value) Value
var Add BinOp = func(a, b Value) Value {

	switch a := a.(type) {
	case StringValue:
		return a + asStringValue(b)
	}
	switch bt := b.(type) {
	case StringValue:
		return asStringValue(a) + bt
	case FloatValue:
		a = asFloat(a)
	case BoolValue:
		b = boolInt(bt)
	}
	switch a := a.(type) {
	case FloatValue:
		return a + asFloat(b)
	case IntValue:
		return a + b.(IntValue)
	case ListValue:
		return a.Add(b)
	case BoolValue:
		return boolInt(a) + b.(IntValue)
	default:
		panic(fmt.Errorf("unsupported type %T", a))
	}
}
var And BinOp = func(a, b Value) Value {
	return boolean(truthy(a) && truthy(b))
}
var Div BinOp = func(a, b Value) Value {
	switch bt := b.(type) {
	case FloatValue:
		a = asFloat(a)
	case BoolValue:
		b = boolInt(bt)
	}
	switch a := a.(type) {
	case FloatValue:
		return a / asFloat(b)
	case IntValue:
		return a / b.(IntValue)
	case BoolValue:
		return boolInt(a) / b.(IntValue)
	default:
		panic(fmt.Errorf("unsupported type %T", a))
	}
}
var Eq BinOp = func(a, b Value) Value {
	if eq, ok := a.(interface {
		Eq(b Value) bool
	}); ok {
		return boolean(eq.Eq(b))
	}

	if _, ok := a.(StringValue); ok {
		b = asStringValue(b)
	} else if _, ok := b.(StringValue); ok {
		a = asStringValue(a)
	}
	return boolean(a == b)
}
var Gt BinOp = func(a, b Value) Value {
	if a, ok := a.(interface {
		Gt(b Value) bool
	}); ok {
		return boolean(a.Gt(b))
	}
	return Lt(b, a)
}
var Gte BinOp = func(a, b Value) Value {
	if a, ok := a.(interface {
		Gte(b Value) bool
	}); ok {
		return boolean(a.Gte(b))
	}
	return Lte(b, a)
}
var Has BinOp = func(a, b Value) Value {
	switch a := a.(type) {
	case ListValue:
		return boolean(a.Contains(b.(ListValue)))
	case StringValue:
		return boolean(strings.Contains(string(a), string(asStringValue(b))))
	default:
		panic(fmt.Errorf("unsupported type %T", a))
	}
}
var Hasnt BinOp = func(a, b Value) Value {
	return Not(Has(a, b))
}
var Lt BinOp = func(a, b Value) Value {
	if a, ok := a.(interface {
		Lt(b Value) bool
	}); ok {
		return boolean(a.Lt(b))
	}
	switch a := a.(type) {
	case FloatValue:
		return boolean(a < b.(FloatValue))
	case IntValue:
		return boolean(a < b.(IntValue))
	default:
		panic(fmt.Errorf("unsupported type %T", a))
	}
}
var Lte BinOp = func(a, b Value) Value {
	if a, ok := a.(interface {
		Lte(b Value) bool
	}); ok {
		return boolean(a.Lte(b))
	}
	return Or(
		Lt(a, b),
		Eq(a, b),
	)
}
var Max BinOp = func(a, b Value) Value {
	if truthy(Gt(b, a)) {
		return b
	}
	return a
}
var Min BinOp = func(a, b Value) Value {
	if truthy(Lt(b, a)) {
		return b
	}
	return a
}
var Mod BinOp = func(a, b Value) Value {
	switch bt := b.(type) {
	case FloatValue:
		a = asFloat(a)
	case BoolValue:
		b = boolInt(bt)
	}
	switch a := a.(type) {
	case FloatValue:
		return FloatValue(math.Mod(float64(a), float64(asFloat(b))))
	case IntValue:
		return a % b.(IntValue)
	case BoolValue:
		return boolInt(a) % b.(IntValue)
	default:
		panic(fmt.Errorf("unsupported type %T", a))
	}
}
var Mul BinOp = func(a, b Value) Value {
	switch bt := b.(type) {
	case FloatValue:
		a = asFloat(a)
	case BoolValue:
		b = boolInt(bt)
	}
	switch a := a.(type) {
	case FloatValue:
		return a * asFloat(b)
	case IntValue:
		return a * b.(IntValue)
	case BoolValue:
		return boolInt(a) * b.(IntValue)
	default:
		panic(fmt.Errorf("unsupported type %T", a))
	}
}
var Ne BinOp = func(a, b Value) Value {
	return Not(Eq(a, b))
}
var Or BinOp = func(a, b Value) Value {
	return boolean(truthy(a) || truthy(b))
}
var Rnd BinOp = func(a, b Value) Value {
	if randSource == nil {
		seedRandom(rand.Uint64())
	}
	lo := int64(a.(IntValue))
	hi := int64(b.(IntValue))
	r := lo + randSource.Int64N(hi-lo)
	return IntValue(r)
}
var Sub BinOp = func(a, b Value) Value {
	switch bt := b.(type) {
	case FloatValue:
		a = asFloat(a)
	case BoolValue:
		b = boolInt(bt)
	}
	switch a := a.(type) {
	case FloatValue:
		return a - asFloat(b)
	case IntValue:
		return a - b.(IntValue)
	case ListValue:
		return a.Sub(b)
	case BoolValue:
		return boolInt(a) - b.(IntValue)
	default:
		panic(fmt.Errorf("unsupported type %T", a))
	}
}

type BoolValue

type BoolValue bool

func (BoolValue) Output

func (b BoolValue) Output() Output

type CallFrame

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

func (*CallFrame) ChoiceCount

func (f *CallFrame) ChoiceCount() int

func (*CallFrame) DeclareLocal

func (f *CallFrame) DeclareLocal(name string, value Value) *CallFrame

func (*CallFrame) GetVar

func (f *CallFrame) GetVar(name string) (Value, bool)

func (*CallFrame) IncChoiceCount

func (f *CallFrame) IncChoiceCount() *CallFrame

func (*CallFrame) IncTurnCount

func (f *CallFrame) IncTurnCount() *CallFrame

func (*CallFrame) ListAll

func (f *CallFrame) ListAll(v ListValue) ListValue

func (*CallFrame) ListInt

func (f *CallFrame) ListInt(origin string, value int) ListValue

func (*CallFrame) PopFrame

func (f *CallFrame) PopFrame() (*CallFrame, Element, Stepper, bool)

func (*CallFrame) PopVal

func (f *CallFrame) PopVal() (Value, *CallFrame)

func (*CallFrame) PushFrame

func (f *CallFrame) PushFrame(returnTo Element, retStep Stepper, isFunction bool) *CallFrame

func (*CallFrame) PushVal

func (f *CallFrame) PushVal(v Value) *CallFrame

func (*CallFrame) PushVarRef

func (f *CallFrame) PushVarRef(name string) *CallFrame

func (*CallFrame) ResetChoiceCount

func (f *CallFrame) ResetChoiceCount() *CallFrame

func (*CallFrame) TurnsSince

func (f *CallFrame) TurnsSince(addr Address) int

func (*CallFrame) UpdateVar

func (f *CallFrame) UpdateVar(name string, v Value) *CallFrame

func (*CallFrame) Visit

func (f *CallFrame) Visit(addr VisitAddr, from Address) *CallFrame

func (*CallFrame) VisitCount

func (f *CallFrame) VisitCount(addr Address) int

func (*CallFrame) WithGlobal

func (f *CallFrame) WithGlobal(name string, value Value) *CallFrame

func (*CallFrame) WithLocal

func (f *CallFrame) WithLocal(name string, value Value) *CallFrame

type Choice

type Choice struct {
	Label              string
	Dest               Element
	Eval               Evaluator
	IsInvisibleDefault bool
}

func Continue

func Continue(output glue.StringWriter, eval Evaluator, elem Element) []Choice

type ChoiceCounter

type ChoiceCounter struct{} // "choiceCnt"

type ChoicePoint

type ChoicePoint struct {
	Dest  Address         `json:"*"`
	Flags ChoicePointFlag `json:"flg"`
}

type ChoicePointFlag

type ChoicePointFlag uint32
const (
	HasCondition         ChoicePointFlag = 0x01 // Has condition?: Set if the story should pop a value from the evaluation stack in order to determine whether a choice instance should be created at all.
	HasStartContent      ChoicePointFlag = 0x02 // Has start content? - According to square bracket notation, is there any leading content before any square brackets? If so, this content should be popped from the evaluation stack.
	HasChoiceOnlyContent ChoicePointFlag = 0x04 // Has choice-only content? - According to square bracket notation, is there any content between the square brackets? If so, this content should be popped from the evaluation stack.
	IsInvisibleDefault   ChoicePointFlag = 0x08 // Is invisible default? - When this is enabled, the choice isn't provided to the game (isn't presented to the player), and instead is automatically followed if there are no other choices generated.
	OnceOnly             ChoicePointFlag = 0x10 // Once only? - Defaults to true. This is the difference between the * and + choice bullets in ink. If once only (*), the choice is only displayed if its target container's read count is zero.
)

type Container

type Container struct {
	Name        string
	Parent      *Container
	ParentIndex *int
	Flags       ContainerFlag
	Contents    []Node
	Nested      map[string]Container
}

func LoadContainer

func LoadContainer(contents []any) Container

func (Container) Address

func (c Container) Address() Address

func (*Container) Find

func (c *Container) Find(name Address) (Element, []VisitAddr)

func (Container) First

func (c Container) First() Element

func (*Container) Root

func (c *Container) Root() *Container

type ContainerElement

type ContainerElement struct {
	Self  Container
	Index int
}

func (ContainerElement) Address

func (e ContainerElement) Address() (Address, int)

func (ContainerElement) Find

func (e ContainerElement) Find(name Address) (Element, []VisitAddr)

func (ContainerElement) Flatten

func (e ContainerElement) Flatten() (*ContainerElement, []VisitAddr)

func (ContainerElement) Next

func (e ContainerElement) Next() (Element, []VisitAddr)

func (ContainerElement) Node

func (e ContainerElement) Node() Node

type ContainerFlag

type ContainerFlag uint32
const (
	RecordVisits   ContainerFlag = 0x1 // The story should keep a record of the number of visits to this container.
	CountTurns     ContainerFlag = 0x2 // The story should keep a record of the number of the turn index that this container was lasted visited.
	CountStartOnly ContainerFlag = 0x4 // For the above numbers, the story should only record changes when the story visits the very first subelement, rather than random entry at any point. Used to distinguish the different behaviour between knots and stitches (random access), versus gather points and choices (count start only).
)

type Divert

type Divert struct {
	Dest        Address `json:"->"`
	Var         bool    `json:"var"`
	Conditional bool    `json:"c"`
}

func (Divert) GetDest

func (n Divert) GetDest(el Element, stack *CallFrame) (Element, *CallFrame)

type DivertTargetValue

type DivertTargetValue struct {
	Dest Address `json:"^->"`
}

type Done

type Done struct{}

Tries to close/pop the active thread, otherwise marks the story flow safe to exit without a loose end warning.

type DupTop

type DupTop struct{} // "du"

func (DupTop) Apply

func (n DupTop) Apply(stack *CallFrame) *CallFrame

type Element

type Element interface {
	Node() Node
	Address() (Address, int)
	Find(Address) (Element, []VisitAddr)
	Next() (Element, []VisitAddr)
}

type End

type End struct{}

Ends the story flow immediately, closes all active threads, unwinds the callstack, and removes any choices that were previously created.

type EndEval

type EndEval struct{} // "/ev"

type EndStringEval

type EndStringEval struct{} // "/str"

type EndTag

type EndTag struct{} // "/#"

type EvalEvaluator

type EvalEvaluator struct {
	Prev Stepper
}

func (EvalEvaluator) Step

func (e EvalEvaluator) Step(stack *CallFrame, el Element) (Output, *Choice, Element, *CallFrame, Stepper)

type EvalFrame

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

func (*EvalFrame) Pop

func (f *EvalFrame) Pop() (Value, *EvalFrame)

func (*EvalFrame) Push

func (f *EvalFrame) Push(v Value) *EvalFrame

type Evaluator

type Evaluator interface {
	Step(Element) (Output, *Choice, Element, Evaluator)
}

type FloatValue

type FloatValue float64

func (FloatValue) Output

func (f FloatValue) Output() Output

type FuncCall

type FuncCall struct {
	Dest Address `json:"f()"`
	Var  bool    `json:"var"`
}

type FuncReturn

type FuncReturn struct{} // "~ret"

type GetVar

type GetVar struct {
	Name string `json:"VAR?"`
}

func (GetVar) Apply

func (n GetVar) Apply(stack *CallFrame) *CallFrame

type GetVisitCount

type GetVisitCount struct {
	Container string `json:"CNT?"`
}

type Glue

type Glue struct{} // "<>"

type IntValue

type IntValue int64

func (IntValue) Output

func (i IntValue) Output() Output

type ListAllFunc

type ListAllFunc struct{} // "LIST_ALL"

type ListCountFunc

type ListCountFunc struct{} // "LIST_COUNT"

type ListDefs

type ListDefs map[string]map[string]int

func (ListDefs) All

func (l ListDefs) All(origin string) ListValue

func (ListDefs) Get

func (l ListDefs) Get(name string) (ListValue, bool)

func (ListDefs) Value

func (l ListDefs) Value(origin string, value int) ListValue

type ListInt

type ListInt struct{} // "listInt"

type ListIntersectFunc

type ListIntersectFunc struct{} // "L^"

type ListInvertFunc

type ListInvertFunc struct{} // "LIST_INVERT"

type ListItem

type ListItem struct {
	Name   string
	Origin string
	Value  int
}

func (ListItem) Add

func (li ListItem) Add(v Value) Value

func (ListItem) Output

func (li ListItem) Output() Output

type ListMaxFunc

type ListMaxFunc struct{} // "LIST_MAX"

type ListMinFunc

type ListMinFunc struct{} // "LIST_MIN"

type ListRangeFunc

type ListRangeFunc struct{} // "range"

type ListValue

type ListValue struct {
	Items   []ListItem          `json:"list"`
	Origins map[string]struct{} `json:"origins"`
}

func ListEmpty

func ListEmpty(origin string) ListValue

func ListSingle

func ListSingle(origin, name string, value int) ListValue

func (ListValue) Add

func (l ListValue) Add(v Value) ListValue

func (ListValue) At

func (l ListValue) At(index int) ListValue

func (ListValue) Contains

func (l ListValue) Contains(v ListValue) bool

func (ListValue) Eq

func (l ListValue) Eq(v Value) bool

func (ListValue) Intersect

func (l ListValue) Intersect(v ListValue) ListValue

func (ListValue) Lt

func (l ListValue) Lt(v Value) bool

func (ListValue) Lte

func (l ListValue) Lte(v Value) bool

func (ListValue) Output

func (l ListValue) Output() Output

func (ListValue) Put

func (l ListValue) Put(origin, name string, value int) ListValue

func (ListValue) Range

func (l ListValue) Range(start, stop Value) ListValue

func (ListValue) Resolve

func (l ListValue) Resolve(defs ListDefs) ListValue

func (ListValue) Sub

func (l ListValue) Sub(v Value) ListValue

func (ListValue) Updated

func (l ListValue) Updated(v Value) Value

type ListValueFunc

type ListValueFunc struct{} // "LIST_VALUE"

type Newline

type Newline struct{} // "\n"

type NoOp

type NoOp struct{} // "nop"

type Node

type Node interface{}

type Out

type Out struct{} // "out"

type Output

type Output string

func (Output) String

func (o Output) String() string

type Outputter

type Outputter interface {
	Output() Output
}

type Pop

type Pop struct{} // "pop"

type ReadCountFunc

type ReadCountFunc struct{} // "readc"

type Seq

type Seq struct{} // "seq"

type SetTemp

type SetTemp struct {
	Name     string `json:"temp="`
	Reassign bool   `json:"re"`
}

func (SetTemp) Apply

func (n SetTemp) Apply(stack *CallFrame) *CallFrame

type SetVar

type SetVar struct {
	Name     string `json:"VAR="`
	Reassign bool   `json:"re"`
}

func (SetVar) Apply

func (n SetVar) Apply(stack *CallFrame) *CallFrame

type StepEvaluator

type StepEvaluator struct {
	Stack   *CallFrame
	Stepper Stepper
}

func (StepEvaluator) Step

type Stepper

type Stepper interface {
	Step(*CallFrame, Element) (Output, *Choice, Element, *CallFrame, Stepper)
}

type StringEvaluator

type StringEvaluator struct {
	Prev Stepper
	// contains filtered or unexported fields
}

func (StringEvaluator) Step

type StringValue

type StringValue string // "^text"

func (StringValue) Output

func (s StringValue) Output() Output

type StringWrappedEvaluator

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

func (StringWrappedEvaluator) Step

type TagEvaluator

type TagEvaluator struct {
	Prev Stepper
}

func (TagEvaluator) Step

func (e TagEvaluator) Step(stack *CallFrame, el Element) (Output, *Choice, Element, *CallFrame, Stepper)

type Text

type Text string

type ThreadStart

type ThreadStart struct{} // "thread"

type TunnelCall

type TunnelCall struct {
	Dest Address `json:"->t->"`
	Var  bool    `json:"var"`
}

type TunnelReturn

type TunnelReturn struct{} // "->->"

type TurnCounter

type TurnCounter struct{} // "turn"

type TurnsSince

type TurnsSince struct{} // "turns"

type UnaryOp

type UnaryOp func(a Value) Value
var Ceiling UnaryOp = floatOp(math.Ceil)
var Floor UnaryOp = floatOp(math.Floor)
var Int UnaryOp = func(a Value) Value {
	return IntValue(a.(FloatValue))
}
var Neg UnaryOp = func(a Value) Value {
	switch a := a.(type) {
	case IntValue:
		return -a
	case FloatValue:
		return -a
	case BoolValue:
		return -boolInt(a)
	default:
		panic(fmt.Errorf("unsupported type %T", a))
	}
}
var Not UnaryOp = func(a Value) Value {
	return boolean(!truthy(a))
}
var Srnd UnaryOp = func(v Value) Value {
	seedRandom(uint64(v.(IntValue)))
	return VoidValue{}
}

type Value

type Value interface{}

type VarRef

type VarRef struct {
	Name         string `json:"^var"`
	ContentIndex int    `json:"ci"`
}

type Vars

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

func (*Vars) Get

func (v *Vars) Get(name string) (Value, bool)

func (*Vars) With

func (v *Vars) With(name string, value Value) *Vars

type VarsFrame

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

func (*VarsFrame) Get

func (f *VarsFrame) Get(name string) (Value, bool)

func (*VarsFrame) Pop

func (f *VarsFrame) Pop() *VarsFrame

func (*VarsFrame) Push

func (f *VarsFrame) Push() *VarsFrame

func (*VarsFrame) With

func (f *VarsFrame) With(name string, value Value) *VarsFrame

type Visit

type Visit struct {
	Address   Address
	IsVisit   bool
	EntryTurn int
	Prev      *Visit
}

func (*Visit) Count

func (v *Visit) Count(addr Address) int

func (*Visit) LastVisited

func (v *Visit) LastVisited(addr Address, turn int) int

func (*Visit) Push

func (v *Visit) Push(addr VisitAddr, from Address, turn int) *Visit

type VisitAddr

type VisitAddr struct {
	Addr       Address
	Flags      ContainerFlag
	EntryIndex int
}

type VisitIndex

type VisitIndex struct{} // "visit"

type Void

type Void struct{} // "void"

type VoidValue

type VoidValue struct{}

func (VoidValue) Output

func (v VoidValue) Output() Output

Directories

Path Synopsis
cmd
gouache command

Jump to

Keyboard shortcuts

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