worked on better integrating existing code w Godot

This commit is contained in:
Bram Dingelstad 2021-11-21 12:40:05 +01:00
parent 7822c7e6b0
commit 13d2391e57
6 changed files with 199 additions and 271 deletions

View file

@ -18,7 +18,9 @@ signal finished
const Constants = preload('res://addons/Wol/core/constants.gd')
const WolCompiler = preload('res://addons/Wol/core/compiler/compiler.gd')
const WolDialogue = preload('res://addons/Wol/core/dialogue.gd')
const WolLibrary = preload('res://addons/Wol/core/library.gd')
const VirtualMachine = preload('res://addons/Wol/core/virtual_machine.gd')
const StandardLibrary = preload('res://addons/Wol/core/StandardLibrary.gd')
export(String, FILE, '*.wol,*.yarn') var path setget set_path
export(String) var start_node = 'Start'
@ -28,13 +30,17 @@ export var auto_show_options = true
onready var variable_storage = get_node(variable_storage_path)
var program
var dialogue
var virtual_machine
func _ready():
if Engine.editor_hint:
return
var libraries = WolLibrary.new()
libraries.import_library(StandardLibrary.new())
virtual_machine = VirtualMachine.new(self, libraries)
set_path(path)
if not variable_storage:
variable_storage = Node.new()
@ -45,43 +51,22 @@ func _ready():
if auto_start:
start()
func init_dialogue():
# FIXME: Move visited count to variable storage
var existing_state
if dialogue != null:
existing_state = dialogue._visitedNodeCount
dialogue = WolDialogue.new(variable_storage)
# FIXME: Remove these lines
if existing_state:
dialogue._visitedNodeCount = existing_state
dialogue.get_vm().lineHandler = funcref(self, '_handle_line')
dialogue.get_vm().optionsHandler = funcref(self, '_handle_options')
dialogue.get_vm().commandHandler = funcref(self, '_handle_command')
dialogue.get_vm().nodeCompleteHandler = funcref(self, '_handle_node_complete')
dialogue.get_vm().dialogueCompleteHandler = funcref(self, '_handle_dialogue_complete')
dialogue.get_vm().nodeStartHandler = funcref(self, '_handle_node_start')
dialogue.set_program(program)
func set_path(_path):
path = _path
if not Engine.editor_hint:
if not Engine.editor_hint and virtual_machine:
var compiler = WolCompiler.new(path)
program = compiler.compile()
virtual_machine.program = compiler.compile()
func _handle_line(line):
func _on_line(line):
call_deferred('emit_signal', 'line', line)
if auto_show_options \
and dialogue.get_vm().get_next_instruction().operation == Constants.ByteCode.AddOption:
and virtual_machine.get_next_instruction().operation == Constants.ByteCode.AddOption:
return Constants.HandlerState.ContinueExecution
else:
return Constants.HandlerState.PauseExecution
func _handle_command(command):
func _on_command(command):
call_deferred('emit_signal', 'command', command)
if get_signal_connection_list('command').size() > 0:
@ -89,39 +74,33 @@ func _handle_command(command):
else:
return Constants.HandlerState.ContinueExecution
func _handle_options(options):
func _on_options(options):
call_deferred('emit_signal', 'options', options)
return Constants.HandlerState.PauseExecution
func _handle_dialogue_complete():
func _on_dialogue_finished():
emit_signal('finished')
func _handle_node_start(node):
func _on_node_start(node):
emit_signal('node_started', node)
dialogue.resume()
resume()
if !dialogue._visitedNodeCount.has(node):
dialogue._visitedNodeCount[node] = 1
else:
dialogue._visitedNodeCount[node] += 1
func _handle_node_complete(node):
func _on_node_finished(node):
emit_signal('node_finished', node)
return Constants.HandlerState.ContinueExecution
func select_option(id):
dialogue.get_vm().set_selected_option(id)
virtual_machine.set_selected_option(id)
resume()
func pause():
dialogue.call_deferred('pause')
virtual_machine.call_deferred('pause')
func start(node = start_node):
init_dialogue()
emit_signal('started')
dialogue.set_node(node)
dialogue.start()
virtual_machine.set_node(node)
virtual_machine.resume()
func resume():
dialogue.call_deferred('resume')
virtual_machine.call_deferred('resume')

View file

@ -0,0 +1,96 @@
extends 'res://addons/Wol/core/library.gd'
const Value = preload('res://addons/Wol/core/value.gd')
func _init():
register_function('Add', 2, funcref(self, 'add'), true)
register_function('Minus', 2, funcref(self, 'sub'), true)
register_function('UnaryMinus', 1, funcref(self, 'unary_minus'), true)
register_function('Divide', 2, funcref(self, 'div'), true)
register_function('Multiply', 2, funcref(self, 'mul'), true)
register_function('Modulo', 2, funcref(self, 'mod'), true)
register_function('EqualTo', 2, funcref(self, 'equal'), true)
register_function('NotEqualTo', 2, funcref(self, 'noteq'), true)
register_function('GreaterThan', 2, funcref(self, 'ge'), true)
register_function('GreaterThanOrEqualTo', 2, funcref(self, 'geq'), true)
register_function('LessThan', 2, funcref(self, 'le'), true)
register_function('LessThanOrEqualTo', 2, funcref(self, 'leq'), true)
register_function('And', 2, funcref(self, 'land'), true)
register_function('Or', 2, funcref(self, 'lor'), true)
register_function('Xor', 2, funcref(self, 'xor'), true)
register_function('Not', 1, funcref(self, 'lnot'), true)
# `visited` and `visit_count` functions
register_function('visited', -1, funcref(self, 'is_node_visited'), true)
register_function('visit_count', -1, funcref(self, 'node_visit_count'), true)
func add(param1, param2):
return param1.add(param2)
func sub(param1, param2):
return param1.sub(param2)
func unary_minus(param1):
return param1.negative()
func div(param1, param2):
return param1.div(param2)
func mul(param1, param2):
return param1.mult(param2)
func mod(param1, param2):
return param1.mod(param2)
func equal(param1, param2):
return param1.equals(param2)
func noteq(param1, param2):
return !param1.equals(param2)
func ge(param1, param2):
return param1.greater(param2)
func geq(param1, param2):
return param1.geq(param2)
func le(param1, param2):
return param1.less(param2)
func leq(param1, param2):
return param1.leq(param2)
func land(param1, param2):
return param1.as_bool() and param2.as_bool()
func lor(param1, param2):
return param1.as_bool() or param2.as_bool()
func xor(param1, param2):
return param1.as_bool() != param2.as_bool()
func lnot(param1):
return not param1.as_bool()
var visited_node_count = {}
func is_node_visited(node = virtual_machine.current_node_name()):
return node_visit_count(node) > 0
func node_visit_count(node = virtual_machine.current_node_name()):
if node is Value:
node = virtual_machine.program.strings[node.value()].text
var visit_count = 0
if visited_node_count.has(node):
visit_count = visited_node_count[node]
return visit_count
func get_visited_nodes():
return visited_node_count.keys()
func set_visited_nodes(visitedList):
visited_node_count.clear()
for string in visitedList:
visited_node_count[string] = 1

View file

@ -1,11 +1,7 @@
extends Node
const DEFAULT_START = 'Start'
const Constants = preload('res://addons/Wol/core/constants.gd')
const StandardLibrary = preload('res://addons/Wol/core/libraries/standard.gd')
const VirtualMachine = preload('res://addons/Wol/core/virtual_machine.gd')
const WolLibrary = preload('res://addons/Wol/core/library.gd')
const Value = preload('res://addons/Wol/core/value.gd')
var _variableStorage
@ -14,42 +10,21 @@ var _program
var library
var _vm
var _visitedNodeCount = {}
var executionComplete
func _init(variableStorage):
_variableStorage = variableStorage
_vm = VirtualMachine.new(self)
library = WolLibrary.new()
executionComplete = false
# import the standard library
# this contains math constants, operations and checks
library.import_library(StandardLibrary.new())#FIX
#add a function to lib that checks if node is visited
library.register_function('visited', -1, funcref(self, 'is_node_visited'), true)
#add function to lib that gets the node visit count
library.register_function('visit_count', -1, funcref(self, 'node_visit_count'), true)
func is_active():
return get_exec_state() != Constants.ExecutionState.Stopped
#gets the current execution state of the virtual machine
func get_exec_state():
return _vm.executionState
func set_selected_option(option):
_vm.set_selected_option(option)
func set_node(name = DEFAULT_START):
func set_node(name = 'Start'):
_vm.set_node(name)
func start():
print('got here')
if _vm.executionState == Constants.ExecutionState.Stopped:
_vm.resume()
@ -65,29 +40,7 @@ func pause():
func stop():
_vm.stop()
func get_all_nodes():
return _program.nodes.keys()
func current_node():
return _vm.get_current()
func get_node_id(name):
if _program.nodes.size() == 0:
return ''
if _program.nodes.has(name):
return 'id:'+name
else:
return ''
func unloadAll(clear_visited:bool = true):
if clear_visited :
_visitedNodeCount.clear()
_program = null
func dump()->String:
return _program.dump(library)
func node_exists(name:String)->bool:
func node_exists(name):
return _program.nodes.has(name)
func set_program(program):
@ -95,32 +48,7 @@ func set_program(program):
_vm.set_program(_program)
_vm.reset()
func get_program():
return _program
func get_vm():
return _vm
func is_node_visited(node = _vm.current_node_name()):
return node_visit_count(node) > 0
func node_visit_count(node = _vm.current_node_name()):
if node is Value:
node = _program.strings[node.value()].text
var visitCount : int = 0
if _visitedNodeCount.has(node):
visitCount = _visitedNodeCount[node]
print('visit count for %s is %d' % [node, visitCount])
return visitCount
func get_visited_nodes():
return _visitedNodeCount.keys()
func set_visited_nodes(visitedList):
_visitedNodeCount.clear()
for string in visitedList:
_visitedNodeCount[string] = 1

View file

@ -1,69 +0,0 @@
extends 'res://addons/Wol/core/library.gd'
const Value = preload('res://addons/Wol/core/value.gd')
func _init():
register_function('Add', 2, funcref(self, 'add'), true)
register_function('Minus', 2, funcref(self, 'sub'), true)
register_function('UnaryMinus', 1, funcref(self, 'unary_minus'), true)
register_function('Divide', 2, funcref(self, 'div'), true)
register_function('Multiply', 2, funcref(self, 'mul'), true)
register_function('Modulo', 2, funcref(self, 'mod'), true)
register_function('EqualTo', 2, funcref(self, 'equal'), true)
register_function('NotEqualTo', 2, funcref(self, 'noteq'), true)
register_function('GreaterThan', 2, funcref(self, 'ge'), true)
register_function('GreaterThanOrEqualTo', 2, funcref(self, 'geq'), true)
register_function('LessThan', 2, funcref(self, 'le'), true)
register_function('LessThanOrEqualTo', 2, funcref(self, 'leq'), true)
register_function('And', 2, funcref(self, 'land'), true)
register_function('Or', 2, funcref(self, 'lor'), true)
register_function('Xor', 2, funcref(self, 'xor'), true)
register_function('Not', 1, funcref(self, 'lnot'), true)
func add(param1, param2):
return param1.add(param2)
func sub(param1, param2):
return param1.sub(param2)
func unary_minus(param1):
return param1.negative()
func div(param1, param2):
return param1.div(param2)
func mul(param1, param2):
return param1.mult(param2)
func mod(param1, param2):
return param1.mod(param2)
func equal(param1, param2):
return param1.equals(param2)
func noteq(param1, param2):
return !param1.equals(param2)
func ge(param1, param2):
return param1.greater(param2)
func geq(param1, param2):
return param1.geq(param2)
func le(param1, param2):
return param1.less(param2)
func leq(param1, param2):
return param1.leq(param2)
func land(param1, param2):
return param1.as_bool() and param2.as_bool()
func lor(param1, param2):
return param1.as_bool() or param2.as_bool()
func xor(param1, param2):
return param1.as_bool() != param2.as_bool()
func lnot(param1):
return not param1.as_bool()

View file

@ -1,28 +1,24 @@
extends Object
const FunctionInfo = preload("res://addons/Wol/core/function_info.gd")
const FunctionInfo = preload('res://addons/Wol/core/function_info.gd')
const Constants = preload('res://addons/Wol/core/constants.gd')
var functions : Dictionary = {}# String , FunctionInfo
var functions = {}
var virtual_machine
func get_function(name:String)->FunctionInfo:
func get_function(name):
if functions.has(name):
return functions[name]
else :
printerr("Invalid Function: %s"% name)
return null
printerr('Invalid Function: %s'% name)
return
func import_library(other)->void:
Constants.merge_dir(functions,other.functions)
func import_library(other):
Constants.merge_dir(functions, other.functions)
func register_function(name: String, paramCount: int, function: FuncRef, returnsValue: bool):
var functionInfo: FunctionInfo = FunctionInfo.new(name, paramCount, function, returnsValue)
func register_function(name, parameter_count, function, returns_value):
var functionInfo = FunctionInfo.new(name, parameter_count, function, returns_value)
functions[name] = functionInfo
func deregister_function(name: String):
if !functions.erase(name):
pass
func deregister_function(name):
functions.erase(name)

View file

@ -8,48 +8,62 @@ const EXECUTION_COMPLETE : String = 'execution_complete_command'
var NULL_VALUE = Value.new(null)
# Function references to handlers
var lineHandler
var optionsHandler
var commandHandler
var nodeStartHandler
var nodeCompleteHandler
var dialogueCompleteHandler
var line_handler
var options_handler
var command_handler
var node_start_handler
var node_finished_handler
var dialogue_finished_handler
var _dialogue
var _program
var dialogue
var libraries
var program
var _state
var _currentNode
var executionState = Constants.ExecutionState.Stopped
var execution_state = Constants.ExecutionState.Stopped
var string_table = {}
func _init(dialogue):
self._dialogue = dialogue
_state = VmState.new()
func _init(_dialogue, _libraries):
dialogue = _dialogue
libraries = _libraries
libraries.virtual_machine = self
func set_program(program):
_program = program
line_handler = funcref(dialogue, '_on_line')
options_handler = funcref(dialogue, '_on_options')
command_handler = funcref(dialogue, '_on_command')
node_start_handler = funcref(dialogue, '_on_node_start')
node_finished_handler = funcref(dialogue, '_on_node_finished')
dialogue_finished_handler = funcref(dialogue, '_on_dialogue_finished')
assert(line_handler.is_valid(), 'Cannot run without a line handler (_on_line)')
assert(options_handler.is_valid(), 'Cannot run without a options handler (_on_options)')
assert(command_handler.is_valid(), 'Cannot run without a command handler (_on_command)')
assert(node_start_handler.is_valid(), 'Cannot run without a node start handler (_on_node_start)')
assert(node_finished_handler.is_valid(), 'Cannot run without a node finished handler (_on_node_finished)')
_state = VmState.new()
#set the node to run
#return true if successeful false if no node
#of that name found
func set_node(name:String) -> bool:
if _program == null || _program.nodes.size() == 0:
func set_node(name):
if program == null || program.nodes.size() == 0:
printerr('Could not load %s : no nodes loaded' % name)
return false
if !_program.nodes.has(name):
executionState = Constants.ExecutionState.Stopped
if !program.nodes.has(name):
execution_state = Constants.ExecutionState.Stopped
reset()
printerr('No node named %s has been loaded' % name)
return false
_currentNode = _program.nodes[name]
_currentNode = program.nodes[name]
reset()
_state.currentNodeName = name
nodeStartHandler.call_func(name)
node_start_handler.call_func(name)
return true
func current_node_name()->String:
@ -59,11 +73,11 @@ func current_node():
return _currentNode
func pause():
executionState = Constants.ExecutionState.Suspended
execution_state = Constants.ExecutionState.Suspended
#stop exectuion
func stop():
executionState = Constants.ExecutionState.Stopped
execution_state = Constants.ExecutionState.Stopped
reset()
_currentNode = null
@ -71,7 +85,7 @@ func stop():
#resume execution if waiting for result
#return false if error
func set_selected_option(id):
if executionState != Constants.ExecutionState.WaitingForOption:
if execution_state != Constants.ExecutionState.WaitingForOption:
printerr('Unable to select option when dialogue not waiting for option')
return false
@ -84,7 +98,7 @@ func set_selected_option(id):
_state.currentOptions.clear()
#no longer waiting for option
executionState = Constants.ExecutionState.Suspended
execution_state = Constants.ExecutionState.Suspended
return true
@ -99,42 +113,26 @@ func resume():
printerr('Cannot run dialogue with no node selected')
return false
if executionState == Constants.ExecutionState.WaitingForOption:
if execution_state == Constants.ExecutionState.WaitingForOption:
printerr('Cannot run while waiting for option')
return false
if lineHandler == null:
printerr('Cannot run without a lineHandler')
return false
if optionsHandler == null:
printerr('Cannot run without an optionsHandler')
return false
if commandHandler == null:
printerr('Cannot run without an commandHandler')
return false
if nodeStartHandler == null:
printerr('Cannot run without a nodeStartHandler')
return false
if nodeCompleteHandler == null:
printerr('Cannot run without an nodeCompleteHandler')
return false
executionState = Constants.ExecutionState.Running
execution_state = Constants.ExecutionState.Running
#execute instruction until something cool happens
while executionState == Constants.ExecutionState.Running:
while execution_state == Constants.ExecutionState.Running:
var currentInstruction = _currentNode.instructions[_state.programCounter]
run_instruction(currentInstruction)
_state.programCounter += 1
if _state.programCounter >= _currentNode.instructions.size():
nodeCompleteHandler.call_func(_currentNode.nodeName)
executionState = Constants.ExecutionState.Stopped
node_finished_handler.call_func(_currentNode.nodeName)
execution_state = Constants.ExecutionState.Stopped
reset()
dialogueCompleteHandler.call_func()
dialogue_finished_handler.call_func()
return true
@ -158,17 +156,17 @@ func run_instruction(instruction)->bool:
#pass it to client as line
var key = instruction.operands[0].value
var line = _program.strings[key]
var line = program.strings[key]
#the second operand is the expression count
# of format function
if instruction.operands.size() > 1:
pass#add format function support
var pause : int = lineHandler.call_func(line)
var pause = line_handler.call_func(line)
if pause == Constants.HandlerState.PauseExecution:
executionState = Constants.ExecutionState.Suspended
execution_state = Constants.ExecutionState.Suspended
Constants.ByteCode.RunCommand:
var commandText : String = instruction.operands[0].value
@ -178,9 +176,9 @@ func run_instruction(instruction)->bool:
var command = Program.Command.new(commandText)
var pause = commandHandler.call_func(command) as int
var pause = command_handler.call_func(command) as int
if pause == Constants.HandlerState.PauseExecution:
executionState = Constants.ExecutionState.Suspended
execution_state = Constants.ExecutionState.Suspended
Constants.ByteCode.PushString:
#push String var to stack
@ -217,7 +215,7 @@ func run_instruction(instruction)->bool:
#push any return value to stack
var functionName : String = instruction.operands[0].value
var function = _dialogue.library.get_function(functionName)
var function = libraries.get_function(functionName)
var expected_parameter_count : int = function.paramCount
var actual_parameter_count : int = _state.pop_value().as_number()
@ -250,20 +248,21 @@ func run_instruction(instruction)->bool:
Constants.ByteCode.PushVariable:
#get content of variable and push to stack
var name : String = instruction.operands[0].value
var loaded = _dialogue._variableStorage.get_value(name)
# TODO: Reimplement variable storage
var loaded = dialogue.variable_storage.get_value(name)
_state.push_value(loaded)
Constants.ByteCode.StoreVariable:
#store top stack value to variable
var top = _state.peek_value()
var destination : String = instruction.operands[0].value
_dialogue._variableStorage.set_value(destination,top)
dialogue.variable_storage.set_value(destination,top)
Constants.ByteCode.Stop:
#stop execution and repost it
nodeCompleteHandler.call_func(_currentNode.name)
dialogueCompleteHandler.call_func()
executionState = Constants.ExecutionState.Stopped
node_finished_handler.call_func(_currentNode.name)
dialogue_finished_handler.call_func()
execution_state = Constants.ExecutionState.Stopped
reset()
Constants.ByteCode.RunNode:
@ -276,17 +275,17 @@ func run_instruction(instruction)->bool:
else :
name = instruction.operands[0].value
var pause = nodeCompleteHandler.call_func(_currentNode.name)
var pause = node_finished_handler.call_func(_currentNode.name)
set_node(name)
_state.programCounter-=1
if pause == Constants.HandlerState.PauseExecution:
executionState = Constants.ExecutionState.Suspended
execution_state = Constants.ExecutionState.Suspended
Constants.ByteCode.AddOption:
# add an option to current state
var key = instruction.operands[0].value
var line = _program.strings[key]
var line = program.strings[key]
if instruction.operands.size() > 2:
pass #formated text options
@ -297,9 +296,9 @@ func run_instruction(instruction)->bool:
Constants.ByteCode.ShowOptions:
#show options - stop if none
if _state.currentOptions.size() == 0:
executionState = Constants.ExecutionState.Stopped
execution_state = Constants.ExecutionState.Stopped
reset()
dialogueCompleteHandler.call_func()
dialogue_finished_handler.call_func()
return false
#present list of options
@ -309,16 +308,15 @@ func run_instruction(instruction)->bool:
choices.append(Program.Option.new(option.key, optionIndex, option.value))
#we cant continue until option chosen
executionState = Constants.ExecutionState.WaitingForOption
execution_state = Constants.ExecutionState.WaitingForOption
#pass the options to the client
#delegate for them to call
#when user makes selection
optionsHandler.call_func(choices)
options_handler.call_func(choices)
_:
#bytecode messed up woopsise
executionState = Constants.ExecutionState.Stopped
execution_state = Constants.ExecutionState.Stopped
reset()
printerr('Unknown Bytecode %s' % instruction.operation)
return false