Help with stacking objects

I 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"?

  1. vitamin
  2. 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.


There 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.


It 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)


The 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


(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)


fascinating idea MrAngel!


Mrangel, how do you drop two vitamins, without having still four vitamins in inventory?


Mrangel, 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]


This topic is now closed. Topics are closed after 60 days of inactivity.

Support

Forums