ink
ink copied to clipboard
What is the correct way of "toggling" items in an ink list from C#?
We've got an ink list that looks like this:
LIST valuesList = Family, Happiness, Love
The docs show how to create a new ink list from C#, but not how to manipulate an existing one. What is the correct way of toggling the items in that list from C#?
Our attempts to get this working have worked, except that for some reason the list isn't saved when exporting story state to json. We're assuming that the reason is that we're referencing the wrong instance of list items, or something.
What we're doing is:
- Getting the full list of InkListItem from the InkList.origins
- Getting the InkList from variableState["valuesList"]
- Using AddItem/Remove to toggle the items in the list.
Is this the correct way to do this?
Are you settingvariableState["valuesList"]
to be your newly modified InkListItem at the end?
I was! It turned out to be a bug, which was fixed. The only safe way to manipulate lists from code is to create a new list and write it to the old list - You should think of them as value types. They are, frankly, bloody awful to work with. It's probably easiest in many ways to do manipulation in ink if you can.
That said, here's some code that I use to manipulate an ink list that's shown to the user as a list of items that can be toggled on and off. It's annoyingly long!
// This clones the list.
var listVar = new InkList(originalList);
listVar.Clear();
foreach(var inkListItem in originalList.all) {
if(originalList.ContainsKey(inkListItem.Key)) {
listVar.Intersect(InkList.FromString(inkListItem.Key.fullName, story));
}
}
// This adds and removes items that have been changed when the user clicks on the togglable buttons
bool changed = false;
foreach(var item in items) {
if(item.isOn && !listVar.ContainsKey(item.inkListItem)) {
listVar.Intersect(InkList.FromString(item.inkListItem.fullName, story));
changed = true;
}
else if(!item.isOn && listVar.ContainsKey(item.inkListItem)) {
listVar.Remove(item.inkListItem);
changed = true;
}
}
// Then we write the new list to the ink!
if(changed)
story.variablesState[listInstruction.listVariableName] = listVar;```
Oh, right. I did come across that too. I have a StoryController
C# class that wraps everything Ink related, so I have a layer of abstraction. My code for setting Ink Lists just works with a string[]
array, so I'd do toggle manipulation elsewhere in my game code, then pass a string[]
array to StoryController
and let it handle writing it back to Ink:
STORYCONTROLLER
public bool SetListValue(string variableName, string listType, string[] items)
{
if (variableName == null || story == null || listType == null || items == null)
{
return false;
}
InkList newList = new InkList(listType, story);
foreach (string item in items)
{
newList.AddItem(item);
}
return SetVariableValue(variableName, newList);
}
public bool SetVariableValue(string variableName, object value)
{
if (variableName == null || story == null || value == null)
{
return false;
}
if (story.variablesState.Contains(variableName) == false)
{
Loggy.LogError("SetVariableValue: Cannot find variable: " + variableName + " in Ink script", LoggyType.InkScript);
return false;
}
story.variablesState[variableName] = value;
return true;
}
Oh boy would you be willing to share it? I'd love to see how others do techy ink stuff!
One thing for future readers - my code should deal with cases where a list contains list items from several difference lists. You may not want that! It's a thing to be aware of that ink lists can do, and it explains a lot of weirdness about the API!
Yeah, I hope to at some point. It also abstracts Ink Flows completely, so different parts of the game can have a Flow object and perform functions on it, without caring about anything else in the system. Then StoryController
handles all the Flow switching and management in Ink; it can turn Flows on and off dynamically depending on game contexts, etc. It's like a thread-management layer. (And the Ink Engine handles rapid Flow switching really well!) Whenever I finally finish the game I'm working on, I'll try and open source this part.