Help with stacking objects
Summa_Civis
15 Oct 2023, 04:42I am really lost on this one. Here's what I did so far.
Create an object called vitamin.
Gave vitamin an attribute called "quantity."
Created a spawn function that generates object when entering a room.
Where I'm having issue is with the TAKE and DROP actions. Once I TAKE the item twice, Quest is asking the player to select which vitamin from a numbered list.
Which "vitamin"?
- vitamin
- vitamin
I really only want this to be a single category and to be able to display the quantity of the object (in this case, the vitamin) into a integer.
mrangel
15 Oct 2023, 12:55There are many ways to do stacking. If you already have a "quantity" attribute, I'd recommend destroying any duplicates.
If you're creating your stackable object using CloneObject
and similar functions (not create
or clone
), it will already have a prototype
attribute pointing to the original. You can use this attribute to check if any other clones of the same object are in the same location.
So your code would look something like:
- whenever this object is moved:
- search for other clones in the same place
- if you find another one:
- add the two quantities together
- destroy the spare object
For the "when this object is moved" function, I would suggest using the script attribute changedparent
, which is run automatically whenever an object is moved. This covers the case of picking up an object you already have one of, and dropping one where another is already there.
The changedparent
function could look like:
if (HasObject (this, "parent")) {
clones = FilterByAttribute (GetDirectChildren (this.parent), "prototype", this.prototype)
if (ListCount (clones) > 1) {
first = ListItem (clones, 0)
second = ListItem (clones, 1)
first.quantity = first.quantity + second.quantity
second.quantity = 0
}
}
This means that a stack of 5 and a stack of 3 will turn into a stack of 8 and a stack of 0.
Then you can use a script attribute named changedquantity
to remove the 0 and make it easy to see how many you have:
if (this.quantity = 0) {
destroy (this.name)
}
else if (this.quantity = 1) {
this.alias = "vitamin"
this.listalias = "vitamin"
}
else {
this.alias = this.quantity + " vitamins"
this.listalias = this.quantity + " vitamins"
}
Changescripts (script attributes starting with changed
) are run by the engine automatically whenever the corresponding attribute changes, so they're good for stuff like this. When the player uses a vitamin, the use script can just reduce its quantity, and trust the changescript to destroy it if that was the last one.
mrangel
15 Oct 2023, 14:55It may also be worth noting that there's sometimes problems with using destroy
, so most people use RemoveObject
instead. But RemoveObject just moves objects outside of any room, so they'll still be there taking up memory and bloating the save file, which can be a problem if you're spawning a lot of these objects. So if destroy
doesn't behave, it might be better to work around it by delaying the destruction of unwanted objects until the end of the turn. Like this:
if (this.quantity <= 0) {
if (HasAttribute (game, "objects_to_destroy")) {
list add (game.objects_to_destroy, this.name)
}
else {
game.objects_to_destroy = Split (this.name)
SetTurnTimeout (0) {
if (HasAttribute (game, "objects_to_destroy")) {
foreach (objname, ListCompact (game.objects_to_destroy)) {
destroy (objname)
}
game.objects_to_destroy = null
}
}
}
}
else if (this.quantity = 1) {
this.alias = "vitamin"
this.listalias = "vitamin"
}
else {
this.alias = this.quantity + " vitamins"
this.listalias = this.quantity + " vitamins"
}
(I used "vitamins" as the object name because that's the example you gave. If you have lots of stackable objects, it might be easier to have attributes for the singular and plural aliases, so you can use the same scripts for all of them, or make it a function)
Ip Man
16 Oct 2023, 02:38The Pixie also created a library for stacking objects if you want to browse/use that system. https://github.com/ThePix/quest/wiki/Library:-Stackable-items
mrangel
17 Oct 2023, 09:31(My favourite method of stacking is not to use a quantity; instead, the stacked objects are placed inside the first one. So you have an object named "four vitamins" which contains objects named "three vitamins", "two vitamins", and "vitamin". Then you can mess around with the background scope script to make the inside objects accessible for commands without them showing up separately on the inventory; so that "drop vitamin" and "drop two vitamins" all work how you would expect; but "drop vitamins" causes the disambiguation menu to ask how many you want to drop. This can be a bit of a pain to implement, but I think it's worth it if you can get your head around the code)
Ip Man
22 Oct 2023, 01:30fascinating idea MrAngel!
Jennifer Wren
28 Oct 2023, 15:41Mrangel, how do you drop two vitamins, without having still four vitamins in inventory?
mrangel
28 Oct 2023, 23:59Mrangel, how do you drop two vitamins, without having still four vitamins in inventory?
In this example, you would have the changedparent script do something like (pseudocode):
- If the old parent is an object with the same prototype
- do its chsngedparent script too
- if there is an object of the same type in the same location
- move one object inside the other
- else
- count how many identical objects are inside this one, and add one
- change the alias of this object accordingly
- if the new parent is an object with the same prototype
- do its changedparent script
Doing the changedparent scripts of both stacks ensures that the numbers are always right.
[Think that's right off the top of my head]