An easy to use Unity 3D library for creating illustrated Interactive Fiction games and more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

288 lines
7.9 KiB

local inspect = require('inspect')
-- Utility functions for working with Lua in Fungus
local M = {}
------------
-- Debugging
------------
-- Prints a summary of object v in a human readable format.
function M.inspect(v)
print (inspect.inspect(v))
end
------------
-- Math
------------
-- rounds a number to the nearest decimal places
-- e.g. round(1.523, 100) -- 1.52
function round(val, decimal)
if (decimal) then
return math.floor( (val * 10^decimal) + 0.5) / (10^decimal)
else
return math.floor(val+0.5)
end
end
-------------
-- Coroutines
-------------
-- Waits for a number of seconds
function M.wait(duration)
local t = M.time.timeSinceLevelLoad
while (M.time.timeSinceLevelLoad - t < duration) do
coroutine.yield()
end
end
-- Waits until the lamda function provided returns true, or the timeout expires.
-- Returns true if the function succeeded, or false if the timeout expired
function M.waitfor(fn, timeoutduration)
local t = M.time.timeSinceLevelLoad
while (not fn()) do
coroutine.yield()
if (M.time.timeSinceLevelLoad - t > timeoutduration) then
return false
end
end
return true
end
-- Starts a C# coroutine method
function M.run(enumerator)
-- If the parameter isn't an enumerator then CreateTask will fail
local status, err = pcall( function()
local task = M.luaenvironment.RunUnityCoroutine(enumerator)
end)
if (not status) then
print(debug.traceback("Can't start a coroutine with a c# method that doesn't return IEnumerator", 2))
error(err)
end
end
-- Starts a C# coroutine method and waits until it's finished
function M.runwait(enumerator)
-- If the parameter isn't an enumerator then CreateTask will fail
local status, err = pcall( function()
local task = M.luaenvironment.RunUnityCoroutine(enumerator)
while (task != nil and task.Running) do
coroutine.yield();
end
end)
if (not status) then
print(debug.traceback("Can't start a coroutine with a c# method that doesn't return IEnumerator", 2))
error(err)
end
end
---------------
-- String table
---------------
-- Set active language for string table
function M.setlanguage(languagecode)
M.luautils.activeLanguage = languagecode
end
-- Get a named string from the string table
function M.getstring(key)
return M.luautils.GetString(key)
end
-- Substitutes variables and localisation strings into a piece of text
-- e.g. v = 10, "Subbed value is [$v]" => "Subbed value is 10"
function M.sub(text)
return M.luautils.substitute(text)
end
--------------------------------------------------------------
-- Integration tests
-- These functions require the Unity Test Tools to work.
--------------------------------------------------------------
-- Checks if a condition is true (requires Unity Test Tools)
-- Lua has a built in assert function, so we called this check to avoid conflicting.
function M.check(c, reason)
if (not c) then
-- Output a traceback to help track down source
error( debug.traceback("Assert failed", 2) )
end
M.test.assert(c, reason)
end
-- Pass an integration test (requires Unity Test Tools)
function M.pass()
M.test.pass()
end
-- Fail an integration test (requires Unity Test Tools)
-- reason: Option string explaining why the test failed.
function M.fail(reason)
error( debug.traceback("Test failed", 2) )
M.test.fail(reason)
end
---------------------------------------------
-- All functions below require Fungus to work
---------------------------------------------
-------------
-- Say Dialog
-------------
-- Options for configuring Say Dialog behaviour
M.sayoptions = {}
M.sayoptions.saydialog = nil
M.sayoptions.clearprevious = true
M.sayoptions.waitforinput = true
M.sayoptions.fadewhendone = true
M.sayoptions.stopvoiceover = true
-- Set the active saydialog to use with the say function
function M.setsaydialog(saydialog)
M.sayoptions.saydialog = saydialog
end
-- Gets the active say dialog, or creates one if none exists yet
function M.getsaydialog()
if (M.sayoptions.saydialog == nil) then
local sd = M.luautils.spawn("SayDialog").GetComponent("SayDialog")
M.setsaydialog(sd)
end
return M.sayoptions.saydialog
end
-- Set the active character on the Say Dialog
-- character: A Fungus.Character component
-- portrait: The name of a sprite in the character's portrait list
function M.setcharacter(character, portrait)
assert(character, "character must not be nil")
local sd = M.getsaydialog()
sd.SetCharacter(character)
-- Do substitution on character name
local subbed = M.sub(character.nameText)
M.sayoptions.saydialog.SetCharacterName(subbed, character.nameColor)
-- Try to set the portrait sprite
if (portrait) then
if (portrait == "") then
M.sayoptions.saydialog.SetCharacterImage(nill)
else
for i,v in ipairs(character.portraits) do
-- Use a case insensitive comparison
if (string.lower(v.name) == string.lower(portrait)) then
M.sayoptions.saydialog.SetCharacterImage(v)
end
end
end
end
end
-- Write text to the active Say Dialog
-- text: A string to write to the say dialog
-- voice: A voiceover audioclip to play
function M.say(text, voiceclip)
local sd = M.getsaydialog()
-- Do variable substitution before displaying text
local subbed = M.sub(text)
local e = sd.SayInternal(subbed, M.sayoptions.clearprevious, M.sayoptions.waitforinput, M.sayoptions.fadewhendone, M.sayoptions.stopvoiceover, voiceclip)
M.runwait(e)
end
--------------
-- Menu Dialog
--------------
-- Options for configuring Menu Dialog behaviour
M.menuoptions = {}
-- Set the active menudialog to use with the menu function
function M.setmenudialog(menudialog)
M.menuoptions.menudialog = menudialog
end
-- Gets the active menu dialog, or creates one if none exists yet
function M.getmenudialog()
if (M.menuoptions.menudialog == nil) then
local md = M.luautils.spawn("MenuDialog").GetComponent("MenuDialog")
M.setmenudialog(md)
end
return M.menuoptions.menudialog
end
-- Display a menu button
-- text: text to display on the button
-- callback: function to call when this option is selected
-- interactive (optional): if false, displays the option as disabled
function M.menu(text, callback, interactive)
local md = M.getmenudialog()
-- Do variable substitution before displaying text
local subbed = M.sub(text)
md.AddOption(subbed, interactive or true, M.luaenvironment, callback)
end
-- Display a timer during which the player has to choose an option.
-- duration: The length of time to display the timer.
-- callback: Function to call if the timer expires before an option is selected.
function M.menutimer(duration, callback)
local md = M.getmenudialog()
local e = md.ShowTimer(duration, M.luaenvironment, callback)
M.runwait(e)
end
-- Clear all currently displayed menu options
function M.clearmenu()
assert(M.menuoptions.menudialog, "menudialog must not be nil")
M.menuoptions.menudialog.Clear()
end
------------
-- Flowchart
------------
-- Returns the specified Variable in a Flowchart.
-- To access the value of the variable, use its .value property. e.g.
-- v = getvar(flowchart, "FloatVar")
-- v.value = 10 -- Sets the value of the variable to 10
-- f = v.value -- f now contains 10
-- flowchart: The Fungus Flowchart containing the Block to run.
-- varname: The name of the Variable to get.
function M.getvar(flowchart, varname)
assert(flowchart, "flowchart must not be nil")
assert(blockname, "varname must not be nil")
return flowchart.GetVariable(varname)
end
-- Runs the specified Block in a Flowchart
-- flowchart: The Fungus Flowchart containing the Block to run.
-- blockname: The name of the Block to run.
-- nowait: If false, will yield until the Block finishes execution. If true will continue immediately.
function M.runblock(flowchart, blockname, nowait)
assert(flowchart, "flowchart must not be nil")
assert(blockname, "blockname must not be nil")
local block = flowchart.FindBlock(blockname)
if (not block) then
error("Block " .. blockname .. " not found")
return
end
local e = block.Execute();
if (nowait) then
M.run( e )
else
M.runwait( e )
end
end
return M