Nomicon is an Ink runtime for Lua, especially LÖVE (though LÖVE is not a requirement). Compile your script with Inklecate and run it with the easy to use Nomicon API!
Add the nomicon folder to your project... somewhere. Run your story like so:
local json = require "json" -- for example, see https://github.com/rxi/json.lua
local Nomicon = require "nomicon"
local book = json.decode(love.filesystem.read("demo.json"))
local story = Nomicon.Story(book)
local choices = Nomicon.ChoiceList(story)
while story:canContinue() do
local text = story:continue()
print(text)
if choices:hasChoices() then
for i = 1, choices:getChoiceCount() do
local choice = choices:getChoice(i)
print(string.format("%d. %s", i, choice:getText()))
end
local choiceIndex
repeat
io.write("> ")
local input = io.read()
choiceIndex = tonumber(input)
if not choiceIndex then
print("Please enter a choice number.")
elseif not (choiceIndex >= 1 and choiceIndex <= choices:getChoiceCount()) then
print(string.format("Please enter a choice from 1 through %d.", choices:getChoiceCount()))
end
until choiceIndex and choiceIndex >= 1 and choiceIndex <= choices:getChoiceCount()
choices:getChoice(choiceIndex):choose()
end
end
print("The End.")See the API documentation in the source or README for more info.
-
Nomicon.Story(book, defaultGlobalVariables = {}): Construct a newNomicon.Storyinstance with the provided book JSON marshalled to a Lua table.defaultGlobalVariablesoverrides global variables initialized in the Ink script - use with care! -
Nomicon.Story:getGlobalVariable(variableName, marshal = true): Gets the current value of a global variable. Ifmarshalis false (it defaults to true), then the underlyingNomicon.impl.Valuewill be returned; otherwise, the direct value will be returned.Nomicon.impl.Valueis read-only and a reference is only valid until the nextNomicon.Story:setGlobalVariablewith that variable name. So clone it if you want to keep it around. -
Nomicon.Story:setGlobalVariable(variableName, value): Sets a global variable. Any global variable listeners will not be fired. -
Nomicon.Story:listenForGlobalVariable(variableName, func, marshal = true, ...): Listens for a change on the global variablevariableName.funcwill be called differently depending on the value forvariableName. All extra parameters will be passed in as the first parameters tofuncusing magic. Iffuncreturns a value, it will be marshalled to a valid Ink value (or error on failure). Ifmarshalis false, the underlyingNomicon.impl.Valueobjects will be provided. Like withNomicon.Story:getGlobalVariable(), these values are only valid during the function call. Clone them if you want to keep them around! Multiple listeners can be attached, but only the return value of the last listener registered will be used to override a variable assignment.- If
variableNamewas"*", thenfuncwill be called like:func(..., variableName, currentValue, previousValue) - If
variableNamewas not"*", thenfuncwill be called like this:func(..., currentName, previousValue).
- If
-
Nomicon.Story:silenceGlobalVariableListener(variableName, func): Silences the global variable listenerfuncforvariableName. Iffuncis true, then all listeners will be silenced forvariableName. -
Nomicon.Story:hasExternalFunction(name): Returns true if an external function with namenamehas been registered; false otherwise. -
Nomicon.Story:freeExternalFunction(name): Removes the external function withname. If it is called by the code, the game will silently returnValue.VOIDwhich may have unforeseen consequences. -
Nomicon.Story:bindExternalFunc(name, func, marshal = true, ...): If an external function withnamehas yet to be bound, then this will bindfuncto name. Like the global variable listeners, all extra values will be passed first tofuncfollowed by the arguments provided by the script. Ifmarshalis true (the default), then these values will be marshalled fromNomicon.impl.Value; otherwise, they will not. The arguments, if they areNomicon.impl.Value, are only valid until the function returns, so if you store them as un-marshalled values, clone them! -
Nomicon.Story:canContinue(): Returns true if the story can continue; false otherwise. -
Nomicon.Story:continue(yield = false): Continues the story. Returns the current line of text and the tags associated with the that line of text. Ifyieldis true, then the story will yield nothing after each "step" (execution cycle: get next instruction, advance pointer, check if done). Until this method returns, any mutations onNomicon.Storyare not allowed. This includes, but is not necessarily limited to:- Setting a global variable or listening for a global variable.
- Binding an external function or freeing an external function.
- Create a new flow, switching the current flow, or freeing a flow.
- Making a choice or choosing a new path.
- Calling a function.
-
Nomicon.Story:hasChoices(): Returns true if there is at least one choice available, false otherwise. This includes unselectable choices. If you only want to manage visible and selectable choices, seeNomicon.ChoiceList. -
Nomicon.Story:getChoice(index): Returns the choice at the provided index. This will be aNomicon.impl.Choice, which is lower-levelled thanNomicon.Choiceand exposes some intrinsic, internal state about the choice. -
Nomicon.Story:getTagCount(): Returns the number of tags associated with the last line of text. -
Nomicon.Story:getTag(index): Returns the tag at the provided index. Negative values will wrap, so passing in-1will return the last tag. -
Nomicon.Story:getGlobalTagCount(): Returns the number of global tags associated with the story. -
Nomicon.Story:getGlobalTag(index): Returns the global tag at the provided index. LikegetTag, negative values wrap. -
Nomicon.Story:getTags(path): Returns an array of the tags that start prior to content atpath. This runs in a separate flow (see below) so the current execution state isn't affected. -
Nomicon.Story:choose(option, ...): Increments the turn count and switches to the knot/container atoption(if the argument is a string) or chooses a specific choice (if the argument is aNomicon.impl.Choice). Any extra arguments are passed are passed onto the stack to the knot. -
Nomicon.Story:call(func, marshal = true, yield = false, ...): Callsfuncwith...args. Returns the content, tags, and any return values (will probably be one, but who knows in the future...). The call is executed in a temporary flow independent of the current flow for safety. Iffuncerrors, this flow will safely be disposed of and then the error will bubble up. Values will be marshalled fromNomicon.impl.Valueifmarshalis true; if not, they will be returned untouched. Keep in mind these unmarshalled values are only valid until the next method that modifies the story state in any way - so clone them if you want to keep them around!yieldmakes this function yield at each instruction cycle while executing the function. -
Nomicon.Story:getTurnCount(): Returns the current turn count of the story. Turn count increments with every action or call toNomicon.Story:choose.
Flows are mostly independent executions of the story. Only global variables, turn/visit counts, external functions, and RNG are shared.
-
Nomicon.Story:newFlow(name): Creates a new flow with the given name but does not switch to it. Returns true if the flow was successfully created (i.e.,namewas not used or"default"), false otherwise. -
Nomicon.Story:deleteFlow(name): Deletes a flow with the given name. The"default"flow cannot be deleted. If this flow is the current flow, will switch back to the default flow. Returns true if the flow was deleted (i.e.,namewas not used or"default"), false otherwise. -
Nomicon.Story:hasFlow(name): Returns true if there is a flow with the given name. -
Nomicon.Story:flows(): Returns an iterator over the order (index) and name of the flows. The order (index) has no functional effect; it just represents the order the flow was created relative to each other flow. -
Nomicon.Story:getCurrentFlowName(): Returns the current flow's name.
Generally you should use the Nomicon.ChoiceList API over directly querying the Nomicon.Story. You'd only want to use Nomicon.Story's choices if you want them to be visible (i.e., greyed out) when unselectable.
-
Nomicon.ChoiceList(story): Constructs a new ChoiceList. The choices will automatically update with every update to theNomicon.Story, even with flow switches, etc. -
Nomicon.ChoiceList:hasChoices(): Returns true if there is at least one selectable and visible option. -
Nomicon.ChoiceList:getChoiceCount(): Returns the count of visible and selectable choices. -
Nomicon.ChoiceList:getChoice(index): Returns theNomicon.Choice(not to be confused withNomicon.impl.Choice) at the index. Negative values wrap around, so -1 would return the last selectable and visible choice.
This is a light-weight wrapper over Nomicon.impl.Choice.
-
Nomicon.Choice:getIndex(): Returns the index into the parentNomicon.ChoiceList(not the same as the index into theNomicon.StorygetChoiceCount()/getChoice()methods!). -
Nomicon.Choice:choose(): Chooses this choice. Internally callsNomicon.Story:choose()with theNomicon.impl.Choicethis wraps. -
Nomicon.Choice:getText(): Returns the visible text of the choice. -
Nomicon.Choice:getTagCount(): Returns the tag count. -
Nomicon.Choice:getTag(index): Gets the tag at the provided index. Like everything else, a negative value wraps, so -1 returns the last tag.
-
Nomicon.Story:getListDefinitions(): Gets the list definitions interface. This allows you to programmatically create lists at runtime, e.g. via external functions or setting global variables. See documentation innomicon/impl/ListDefinitions.lua. -
Nomicon.Story:setRandom(setSeedFunc, getSeedFunc, random): Override the default RNG. Nomicon will use LÖVE's RNG internally if available with the default seed.setSeedFunctakes a number and sets the seed;getSeedFuncreturns the current seed that can be set insetSeedFunc; andrandomtakesminandmaxand returns a number from[min, max]. -
Nomicon.Story:setRandomSeed(seed): Sets the random seed of the RNG. -
Nomicon.Story:getRandomSeed(): Returns the current random seed from the RNG. -
Nomicon.Story:random(min, max): Returns a random integer (inclusive) between min and max.
This project is licensed under the MPL. View LICENSE in the root directory or visit http://mozilla.org/MPL/2.0/ for the terms.