Developing a Twine game; random assorted questions

Hello!
I’m developing a rather ambitious Twine game using Sugarcube 2.35. I’ve run into some roadblocks, so I’d love to post here when I need help!

Currently, I’m dealing with two main issues:
First, I can’t figure out how to make <<links>> type out text. I’m looking for a sort of middle ground between a link-replace and a normal link. What I’m attempting to accomplish is a button to pick up an item; <<link "Pick up item.">>. It runs an <<if>> statement when clicked, checking if you have enough inventory space. If you have space, you’ll pick it up. If you don’t have space, then it’ll say “You don’t have enough inventory space!,” yet the link still remains (hence why it can’t be a link-replace). Any ideas on how I can do this? Placing simple text or even a print function into the <<else>> of the if statement inside the link doesn’t seem to do anything.

Secondly, I’m trying to make a digestion system. If you eat something, it increases your “fullness;” if you’re too full, you can’t eat something that goes over your fullness (an idea that also suffers from the previous issue). When you eat something, it’s added to an array, with an HP stat and a weight stat. Every digestion tick (custom system I’ve already made), the first item in the aforementioned array will lose 1 point of HP, and will add 1x its weight stat to the player’s weight. My question here is, how can I make an array, where each index has its own stats? My idea was to simply make a nested array, but then I don’t really know how to select the first index of the digestion array, and then the first index of whatever’s being digested. Would it be something like $digestion[0[0]]? I also need to be able to read the first value of all these nested indexes, as the HP will be added up in order to calculate your fullness. Though this seems pretty straightforward, as once I know how to read nested arrays, I just need to take digestion.length and use a <<for>> loop to add the HP values of each item inside digestion.length. EDIT: I’ve figured this one out! Although I’m leaving it here in case anyone has feedback to give on it–maybe I’m making some huge mistake programming it this way? ;0

I greatly appreciate any help I can get! Thank you so much for your time :slight_smile:

1 Like

My basic advice would be not to fight with the basic operation of Twine:

  1. Parse/evaluate the passage including variables and conditions
  2. Render the reslting passage to the user by replacing the contents of the viewed HTML
  3. Wait for interaction
  4. Choose the next passage and loop back to step 1

Anything that changes the current page without going through the loop is challenging to do.

For both the inventory and the eating a simple method is to test whether the item can be used and then either create text that says “Object: you are carrying to much to pick it up” or a link “Object: pick up”. This is actually better interaction for the user too: there’s no frustrating clicking on things and “computer says no” moments.

For the digestion thing I’d suggest using objects rather than multi dimensional arrays. This may be easier by dipping into JavaScript though. A food object could have attributes of name, hp, weight, and then you’d just need a simple array of those. You can create them like this:

<<set $food = {
    name: "donut",
    hp: 2,
    weight: 20
} >>
1 Like

Thank you for the inventory idea! I’ll definitely go ahead and add that :slight_smile: (I actually think I tried it before and still have the code, but was just really stubborn on doing this one thing. Oops!).

Don’t worry, I’m using an object to track the player’s stats and features (e.g $plrFeats.weight or $plrStats.STR). As for digestion, I’d considered that, but my thought process is that you can eat multiples of the same object. For instance, you can eat multiple “Test Apples.” So when digesting, if I said “subtract testApple.hp,” it’d subtract it across all test apples within the digestion array, regardless of whether or not they’re the one being currently digested.
I suppose I could make this system work via when you eat an item, the name of that item is put into the ‘digesting’ array, and then every digestion tick, it simply takes the name that’s in the array and inserts it into a (NAME).hp and does stuff to it; and then once the item is fully digested, the (NAME).hp resets, but that’d be more work than it’s worth, since I’d have to create a new object or object perimeter for each edible item.
Also if you’re wondering, I did get the nested arrays idea put together, and it works without issue! :slight_smile:

Multiple objects of the same type are always interesting. The simple answer is to have separate objects for each one, each with their own distinct attributes. Having a copy constructor to clone them from an original helps. This means anything that lists objects has to then count objects by “type” - in general you don’t want the player’s inventory or a location to read “an apple, an apple, an apple…”.

The other way is to have an object that is a count, and a reference to the original. It makes the descriptions simpler, but get/drop/consume more complex. If you then want to then mutate one item, you probably need to follow a copy-on-write pattern: once you use a setter on a value it creates a copy of the original items attributes locally before changing the local value.

Most Twine games tend to be simple enough that there’s only one of a thing, or they just keep a count of a very small number of items. Having said that there are pre-built inventory systems you can use instead of rolling your own.

I’m afraid I have…absolutely no idea what you’re talking about here :sweat_smile: I’m sorta new to Sugarcube and have never touched Javascript, so I only know about half the stuff that’s present in the Sugarcube documentation.

Could you explain a little? What’s a copy constructor; are you implying I can clone variables on command? (i.e <<clone $variable>> results in $variable and $variableClone1)

I’m not exactly sure what you mean by “Doing this means anything that lists objects will have to count objects by ‘type,’” as in how doing whatever will make the entire system work differently; though on an off note, I do get that you’re implying that the inventory should say something like “You have: Apple (x6), Torch (x2).” I agree that’s a much cleaner interface that I stupidly hadn’t considered!

For your second paragraph…you’ve completely lost me. :fearful: So you’re suggesting I make a generic object that somehow counts the amount of a specific item thats in my inventory, and somehow references the original, then somehow mutates an item (I assume this means “if you wanted to reduce the HP of one of the items”) using a…“copy-on-write” pattern? And what do you mean by ‘locally;’ why wouldn’t it be global? Etc etc. If I’m not making any sense it’s because I have no idea what’s going on, haha!

Also, as for using a pre-built inventory system…I’ve got a weird kind of laziness. I’d rather build my own from the ground up just so I don’t have to learn how to understand someone else’s, haha! It comes with the bonus of being able to tailor it exactly how I need it. Of course, I understand doing things in this way kind of stifles my growth, since it’s probably a good idea to study other peoples’ code so I can learn better habits, but…I’ll save that for a better day ;).

Okay, sorry. It might be worth looking into JavaScript, as it feels like you are writing something more complex than the typical Twine game.

In general terms a constructor is used to generate a new instance of an object with parameters. A copy constructor takes a single parameter of the same type as the object being created. The implementation usually assigns the fields of the original to it’s own fields. You end up with two objects, the original and the copy, and changing fields in one won’t affect the other. You can find more about this specific to Twine/SugarCube/JavaScript here.

The added complexity comes in that section of the manual comes from the way Twine uses JSON to create save files. These save the variables and fields, but not any methods associated with the object.

SugarCube has a clone() function that you can use to make copies of variables.

<<set $foodCopy to clone($food)>>

If later in the code you change the foodCopy.hp value, the food.hp remains unchanged as they are seperate objects now.

On inventory systems: I’m trying to describe two approaches:-

The obvious way: The inventory is simply a list (or array) of all the things the player is carrying. If they have picked up 10 apples it’s 10 entries long. It’s easy to program get/drop/eat. However if you want a passage that lists the inventory it has to do some work to count how many of the entries are apples (or whatever) - probably using a Map of name to count.

The other disadvantage is that if the player has 1,000 apples the list is 1,000 entries. That’s may be a problem as the save game will be larger, and you may run up against browser local storage restrictions (Chrome tends to be the worst in this regard).

The other way: Instead of a list of items, you have a list of objects that have a count and an item. A player carrying 10 apples has a single entry in the list and that entry has a count of 10 and the item is the apple object. When the player picks up an apple you have to first check if they are carrying any apples. If they are you just increment the count, if not you have to add another entry to the list with a count of one. When they drop/eat one then you have to check if the count is > 1. If it is you decrement the count, but if it is one you have to remove the entry (or be really careful about entries with a count of zero). It does make the inventory passage easier though.

In this second approach you have to be careful if your game has say one “poisoned apple”, and the player doesn’t know it’s different from the other apples. You don’t want the inventory passage to read “10 apples, 1 poisoned apple”, rather “11 apples”.

Copy-on-write (COW) is a general programming technique. In your digestion system, the original un-digested items can just be the original objects - nothing has happened to them yet. Once you start disgesting them and changing their values you need to create a copy so as not to alter all the apples - this would be an example of copy on write. The alternative is to create copies (clones) as soon as you eat them.

Variable scope is a bit odd in Twine. There’s the global State object which carries with it all the “global” and “local” variables in the story and which passages have been visited. But is also carries the history (for when you use the back button), so it also has older copies of those variables too (sometimes called the skein in other IF systems). Then there’s the browser’s Window object which is global to the whole game and isn’t rewound by going back, or persisted in a saved game. The other big difference between the two is that the State is largely updated during the evaluation phase before the passage is rendered to the user, while Window is “live” and affected immediately.

If/when you start messing with JavaScript you’ll often see the game set-up adding functions and other things to Window rather than State as loading a saved game will silently remove any functions added to State due to the way save games work.

I like Twine, but it’s unfortunate that once you try and go beyond simple to more complex stuff there’s suddenly much more to learn and understand.

Yo! Just wanted to let you know that I’m taking a quick little break from this, but I’ll get back to it soon and respond to your message :slight_smile: sorry to keep you waiting!

Heya, I’m back! Lets hope I can pace myself a bit better this time so I can avoid burnout. I’ll list any questions I have below as I read your reply.

While discussing copy constructors, you provided this example:

Isn’t this the same as just setting one variable as another? E.g “set $foodcopy to $food”
Additionally, how would this work if I wanted to set up a digestion array? I imagine you’re saying I should make a clone of the stats of whatever food object I’m eating, and then append that to the digestion array? E.G if I eat an apple, it will process “append clone($applestats) to $digestion”

Thank you for your inventory idea! I’m currently rewriting the testing code, and one of my goals was to rewrite the inventory system. I’ll keep this in mind!
Though I do have one question: I’m assuming you’re telling me that my inventory list should be an object variable such as $inventory, and that to add items to the inventory I should pin parameters on it dynamically. E.G if I pick up an apple, it’d somehow append “apple”:“1” to the inventory object. How can I do this? How would I remove it?
I can also interpret your message as you requesting I have an array in order to list inventory items, and each individual item would be an object of its own; e.g $apple would have $apple.name and $apple.count. How would I list my inventory using this system, though?
Additionally, there are plans to add ‘tampered’ objects to my game. How would I make the tampered object read as though it’s a normal object, while still retaining its individual effects? (E.g when a normal apple is used, it’ll add 30 food and then delete itself; when a tampered apple is used, it’ll add 50 food and then delete itself).

No, it isn’t.
When dealing with objects, the object isn’t stored in the variable. The object is somewhere on “the heap”; a chunk of memory. The variable is a “reference” to that bit of memory. This is perhaps an oversimplification, but:

On the left is assignment. Both variables reference the same object; there’s only one. So if you change $foodCopy.cal it changes the cal attribute of the one Food object, and $food.cal will see the new value.

On the right is cloning. A new object is created in the heap that starts out identical to the original and $foodCopy is a reference to the cloned one. Changing $foodCopy.cal will no longer be seen in the $food one as the two variables now reference separate bits of memory.

For your digestion which starts changing cal, you need to clone the item first so it doesn’t affect all the apples.

I’ll get to the inventory bit later…

Thank you so much for your help. I tend to get confused easily, so sorry if this has been a hassle for you.

When explaining cloning v assigning, you said this:

Note: I blurred this text because I think it’s incorrect, but want to keep it here just in case.

What you’re saying is, if I set variable A to have the value of variable B, and then edit var A, it’d change the values of both var A and B?
This seems a little strange to me. I know Twine and Sugarcube are both different languages, but from my coding experience in the past, it’s never worked that way. Setting var A to the values of var B will just have var A copy the value of var B. If var A was 0 and var B was 3, and you set var A to var B, then var A would be 3 and var B would be 3 as well. Then if you wanted to say, subtract 1 from var A, then it’d equal 2, while var B would still equal 3.

Alternatively, reading a bit closer, I’m beginning to think that normal variables and ‘objects’ work very differently; a concept I didn’t know before. You said that object ‘variables’ simply reference a bit of memory, sort of like a door leading into a room. If I were to copy var A to var B, then instead of creating a new chunk of memory, it’d just reference the same bit of memory–now the room has two doors leading into it. If I go in through door B and paint the room red, leaving the room and coming back in through door A will be the same. Is this correct?

The question still stands, though: how would I go about using this in a digestion array, for a bunch of different foods? Would I have to create a new object for each different food (e.g $apple, name:apple, cal:30), or would I simply change the properties of $food when eating an apple, and then have food be cloned and added to the array afterwards? Though that creates another problem, because I could eat and apple, then a burger and a waffle, all of which would change the properties of $foodCopy. So when added to the array, it’d change all the $foodClone-s in the digestion array. Since I ate the waffle last, then the $foodclone-s for the apple and burger would be changed as well, so it’d be the same as if I ate 3 waffles. It’d also reset any digestion done on the current item.
The only way of getting around this that I can currently think of is to have the digestion array just be an array of strings or something. When it’s time for something to be digested, the string is read and changes the properties of $food accordingly, which are then cloned to $foodCopy in order to be modified…?
As it stands, I’m currently imagining a digestion array where items you’ve eaten are appended to the array. If I were to do this with variables (including objects), then each item eaten would have to have its own unique variable. E.G “apple1, apple2, waffle1, waffle2, waffle3.” I’ll put together a quick video on how my previous digestion array worked.

Again, sorry if this has been a chore for you. I appreciate all your help.

https://drive.google.com/file/d/1LoMUndyqtid0iKct6EZooGgexG7-8H6y/view?usp=sharing

I rather like this analogy! Yes, if your doors are the variables, and the room is the object in memory, it’s correct. Writing $b = $a is making b reference the same place as a.

The next big question is not the digestion bit, but how to do an inventory. Obviously the player has one. But do rooms? In a traditional text-adventure a room is a type of “container” (which things can be put in) and the player can pick things up (say 6 apples) from one place and drop some of them (three apples, and a banana found elsewhere) in another. Most twine games don’t allow this as it’s complex; if you only allow the player to pick stuff up and never drop them then rooms don’t need to have their own inventory and you can get away with a simple link that only ever adds apples to the player’s inventory once.

What does your game need to tell your story?

  • Roughly how many different foods (or other items) are there in the game?
  • How many of each of them might the player have at once?
  • Can the player drop them in a room and come back for them later?
  • Does the player ever have an item (like a bag, a bucket) other items can be put in?

Also, now you’ve got object variables understood, how to represent multiples of the same object (six apples)?

You could have an array with six entries referencing six different apple objects.

Or the six entries could all reference the same apple object - afterall until you start eating them they are identical.

Or their could be one entry in the array referencing an object (InvItem) which has an item field referencing the apple and a count field that’s just the number 6.

Or, there’s a strange way because JavaScript objects behave like what other programing languages call Sets: where one object (the key) is used to find another (the value). If you’ve one apple object $apple, then setting six apples in the inventory could be $inv[$apple] = 6.

All this really needs another diagram I’m afraid, but it’s rather late for me.

As I understand it your digestion array is a list of all the things being digested. Each entry would likely need to be a clone of the original food object, as the part digested items are different to the originals now. $gut[index] = clone(appleObj)

I plan for a fairly moderate amount of items in the game. Various foods, armours, tools, quest items, etc. Nothing crazy, but enough to provide variation. I’d say maybe around 100 unique items combined?

A player can have as many of any item as they want, provided they can acquire it (so if it’s a quest item, they’d only really have as many that are given to them–e.g “5 proton batteries”). This means I could go to a shopkeep and buy 20 stun batons if I wanted.

I do have a battle system worked out, and enemies can sometimes drop items. Items will sometimes be found in areas. The player can also drop items in any area (well, any area they can access their inventory from). So yes, I suppose rooms will require their own inventories–but this shouldn’t be too difficult. Once I get the player inventory down, it’s as simple as making a few small modifications and then copy-pasting it to every room that items can drop or spawn in! There won’t be too many rooms, and you suggested using an object instead of an array for the inventory in order to prevent memory issues, so it shouldn’t be too much of a burden on memory.

If the player can just drop items on the floor, adding a storage chest would be redundant.

So are you suggesting I have an array of $food-s, and then once it’s an items turn for digesting, that index in the digestion array is replaced with a $foodClone? I could see that working, yes :slight_smile:

But again, when dealing with digestion, the issue still stands with needing to make a new variable for each food. If I have like 30 different foods in my game, that’s 30 different objects I need to make. In my current system, when you eat something, it simply runs code that adds hand-typed values to the array; if you eat an apple, clicking “eat” will run “append [5 hp, 5 calories] to $digestionArray.”
If I wanted to do this with variables, I’d essentially be doing the same thing, but just appending $apple to $digestionArray. I think it’d also unnecessarily take up memory (to store all those variables).

These would work with an inventory system, but not a digestion system, since digestion needs to memorize the order the player ate things in.

Yes, my digestion array is a list of everything currently in line to be digested. Only the first item in the array is being digested at any given moment. If you eat an apple then a burger, you’d have to wait for the apple to finish digesting before the burger begins. This was the most effective system I could design after experimenting with some other systems.
Since items aren’t affected until they’re first in line, this means if I used variables, I’d have to keep the ‘digested’ variable different than all the others (e.g “appleCopy, apple, apple”). If it was “appleCopy, appleCopy, appleCopy,” then the health of each appleCopy would be subtracted at the same time. You’d digest all 3 apples in 5 ticks, but only gain the calories of one apple (the system adds weight based on the first index’s “caloric value,” and then subtracts hp from the first index’s “hp.” An object’s hp also determines your fullness, so if you digested 3 apples at once, you’d lose 3 fullness per tick instead of just 1).

Sleep well!

BTW, so my current understanding of the inventory system you’re suggesting is to use an object where each parameter ($inventory.apple) stores an item’s name and count. I’m still curious on how I’d add new parameters to inventory. Could I get away with setting and unsetting parameters whenever you pick up/don’t have an item?
I.E picking up a sword will determine if you do or don’t already have one. If you do, it’ll simply add 1 more sword to $inventory.sword. If you don’t, it will create the parameter $inventory.sword and give it the value of 1, since that’s how many swords you have. If you then dropped that sword, a check would be ran to determine if you have any more swords left, and if you don’t, it’d run “forget $inventory.sword” or whatever that command is. Would this work?

Additionally, returning to the “tainted items” issue, I don’t know how this would work with items that are meant to display as one item yet have different effects. If I had “sword” and “cursed sword,” I’d want it to display as “two swords.” And then assuming the character interacts with their inventory via links, how would I have the link “sword” activate the effects of “cursed sword?”
My current system uses an array for the inventory, where each item in the array refers to a passage. When clicking on the link for apple, it takes you to the passage “itemApple.” If I wanted to add a tainted apple, I could still have the passage link read “apple,” but it’d instead refer to the passage “taintedApple.”

Finally, I seem to be having issues displaying the parameters of an object variable. If I had a $character object with the values hair:long, colour:blue and tried to print $character, instead of displaying “hair:long, colour: blue” it just displays “object[Object]” and I’m not sure why.

I’m considering a system where items added to the inventory “object” will all have a name, count and id. This way you could have a tainted apple and normal apple both named “apple,” but one has the id of “tainted apple” which allows it to execute its special code.
But, thinking about it again, I still have no idea how I’d handle separate IDs under the same listing. I could do something like “if you have a tainted object, have it be the first item used,” but that could be annoying if the player picks up the tainted apple and then 3 other normal apples. I could maybe find a way to implement an array, so you could determine in what order you picked apples up, but…eh. So I’ve got two options: either A, continue using the old system where the inventory reads “an apple, an apple, an apple,” or B, just make the tained apple its own unique named object (e.g “perfectly plump apple”).

Do you think using an object class for the inventory would be a good call from a technical standpoint? (Something about reducing memory usage?) Or should I just stick with my old system that works? Thank you so much.

I’m worried that I might be misunderstanding something here, so please allow me to recap:

I currently have an inventory system that uses an array to list items the player has. There is an inventory cap, but the player can also drop things on the ground (a system that hasn’t yet been implemented, as I need to allow rooms to have their own respective inventories).
I also handle digestion using an array, in order to make sure things eaten by the player are digested one-by-one in the order they were eaten.

You’re suggesting that, instead of using arrays for both, I use generic objects; I.E instead of having $inventory[apple, apple] you think I should have $inventory{apple:2}. Doing it this way will prevent bugs caused by browser memory restrictions.
But, the downside of that is that once I get along to making the digestion mechanic, I’ll have to find out how to A: digest things in the order the player ate them in, and B: I’ll have to make a new variable for every food item in the game, so that I can then clone it and add it to the $digestion object (e.g $digestion{ appleClone}).
Alternatively, if I’m misunderstanding using an object for the digestion mechanic, you’re suggesting that I still use an array, but instead of using a nested array, I use object variables for each food item. I.E my current system looks like this: [[5,5,"apple"],[5,5,"apple"]. You’re suggesting instead I use [$appleClone] and then when digesting simply run $appleClone.hp-= and $weight+=$appleClone.calories.

Addition: Reading previous posts, you also suggested I use an array for the inventory, but instead of having the inventory read “an apple, an apple” I use a "Map of name to count." You explain that the issue with this system is that, if there’s no limit (or a very high limit) on inventory space, this will bloat the save and cause local storage restrictions.

Addition #2: I may be wrong about you suggesting that my inventory should be an object. As the “other way” you suggested after the one I spoke about above, you say that instead of having an array with multiple entries (e.g [apple, apple, apple, pear]) I instead have an array of objects (e.g [$apple, $pear]), where each object variable has a count and a string for the display name.

Addition #3: You elaborate upon both of these ideas, as well as a few more on your post from the 22nd. Option 1 is to have an array with many entries of the same object; [apple, apple, apple]. Option 2 is to do the same, but using…variables or something for some reason? Option 3 is to have an array where each entry is a different item; e.g [$apple, $cucumber] where the number of an item is a property of said variable (I.E $inventory[$apple.count]). Option 4 is similar, but instead of using an object variable, you’re suggesting I use a normal variable that stores a number (its count).

Addition #4: At the end of the post from the 22nd, you wrote the code $gut[index] = clone(apple). How would this work if I had eaten multiple apples/in between other foods? What I’m asking is, is each clone unique? And show do I address those clones; what are they named, and will they be actual global variables ($appleClone1, $appleClone 2)?

At the end of the day, my goal is this:
Inventory system:

  • Ability to drop things in rooms (likely with a global cap). I can also replace this system with a chest at the home base that stores up to 300~ objects (or individual objects if I use variables in the inventory array). Though doing the latter will require that I edit battles to only allow drops in a special ending passage, before they’re lost forever (if you’ve ever played Darkest Dungeon, something like that).
  • Player can carry an undetermined amount of items–likely a cap of 50, but starting at around 10.
  • Easy to use, and preferably as little work and few variables as possible. (This is why I could consider doing something like an object for the inventory, and then simply setting/unsetting parameters whenever you pick or drop something; e.g pick up apple → set $inventory.apple+=; though this depends on me being able to initialize and set object parameters that don’t yet exist, instead of having to add them all to $inventory beforehand.

Digestion system:

  • Remembers order player ate things in
  • Digests things one-by-one, deleting them once their HP reaches 0 and moving to the next item in line
  • Again, as easy to use as possible, avoiding unnecessary clutter (e.g new object variables for EACH food item, that contains their hp and calories)

Please correct me where I’m wrong (or right), as I’d love to get this issue handled asap :slight_smile: I apologise, for I’m very easily confused.

Sorry, I’ve been really busy, and it make take me a few days to reply to all this!

1 Like

Oh alright, no problem! Thank you so much for letting me know, (I was a little worried you might’ve gotten frustrated with me and given up haha). Take your time!

Additional question:
I’ve been experimenting with making a TRPG using Twine. At this point I know I should just start using an actual game engine, but this is fun to me. Keeps things interesting :slight_smile:
Anyways, my current issue is that I don’t know how to implement keyboard controls. The goal is that the player will be able to use the arrow keys to control a character on a tile grid (I’ve already got the tile grid working, I just need to get the controls working so I can then start working on making the controls control the tile grid ID values, aka the ‘sprite’)
I’ve been scouring the internet and eventually decided on using a script macro to throw in some javascript that listens for keydowns and which key you pressed, but from here I’m unable to change story variables through javascript. I tried the normal <<set>> and I’ve tried state.setvariable.(variable name) or whatever the heck that one command is.

Additionally, would you mind if we took this to private messages? Just got into a tangle on the actual Twine forums because I asked too many questions and the guy helping me got tired of it. You seem to be the only one messaging here anyways, so it wouldn’t be much different.
I’ll also try to ask fewer questions, to avoid the same situation. I deeply appreciate your help and don’t want to lose it / wear you out, haha