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 M.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) -- Default bool parameters are tricksy if (interactive == nil) then interactive = true end md.AddOption(subbed, interactive, 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") 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. -- commandindex: Index of the command to start execution at -- nowait: If false, will yield until the Block finishes execution. If true will continue immediately. function M.runblock(flowchart, blockname, commandindex, 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(commandindex or 0); if (nowait) then M.run( e ) else M.runwait( e ) end end return M