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.Story
instance with the provided book JSON marshalled to a Lua table.defaultGlobalVariables
overrides global variables initialized in the Ink script - use with care! -
Nomicon.Story:getGlobalVariable(variableName, marshal = true)
: Gets the current value of a global variable. Ifmarshal
is false (it defaults to true), then the underlyingNomicon.impl.Value
will be returned; otherwise, the direct value will be returned.Nomicon.impl.Value
is read-only and a reference is only valid until the nextNomicon.Story:setGlobalVariable
with 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
.func
will be called differently depending on the value forvariableName
. All extra parameters will be passed in as the first parameters tofunc
using magic. Iffunc
returns a value, it will be marshalled to a valid Ink value (or error on failure). Ifmarshal
is false, the underlyingNomicon.impl.Value
objects 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
variableName
was"*"
, thenfunc
will be called like:func(..., variableName, currentValue, previousValue)
- If
variableName
was not"*"
, thenfunc
will be called like this:func(..., currentName, previousValue)
.
- If
-
Nomicon.Story:silenceGlobalVariableListener(variableName, func)
: Silences the global variable listenerfunc
forvariableName
. Iffunc
is true, then all listeners will be silenced forvariableName
. -
Nomicon.Story:hasExternalFunction(name)
: Returns true if an external function with namename
has 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.VOID
which may have unforeseen consequences. -
Nomicon.Story:bindExternalFunc(name, func, marshal = true, ...)
: If an external function withname
has yet to be bound, then this will bindfunc
to name. Like the global variable listeners, all extra values will be passed first tofunc
followed by the arguments provided by the script. Ifmarshal
is 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. Ifyield
is 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.Story
are 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.Choice
and 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-1
will 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, ...)
: Callsfunc
with...
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. Iffunc
errors, this flow will safely be disposed of and then the error will bubble up. Values will be marshalled fromNomicon.impl.Value
ifmarshal
is 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!yield
makes 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.,name
was 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.,name
was 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.Story
getChoiceCount()/getChoice()
methods!). -
Nomicon.Choice:choose()
: Chooses this choice. Internally callsNomicon.Story:choose()
with theNomicon.impl.Choice
this 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.setSeedFunc
takes a number and sets the seed;getSeedFunc
returns the current seed that can be set insetSeedFunc
; andrandom
takesmin
andmax
and 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.