Stackable items please!

DarkBlueMonkey
06 Jun 2017, 19:18

I'm trying to make an RPG with health packs and so forth. Unfortunately, if the player "hordes" the packs, the UI gets VERY cluttered.

Select which pack to use:

  1. Health Pack
  2. Health Pack
  3. Health Pack
  4. Health Pack
    .
    .
    .

And you can imagine what the Inventory looks like if you have four or five of these, like Ammo and stuff....

I've tried to use the 3rd party "stackable" library, but it doesn't seem to work properly. Please please please could you include the capabilities of the stackable library into the stock product? It seems like such a useful thing, I'm not sure why it's not more widely requested :)

Many thanks in advance!


hegemonkhan
06 Jun 2017, 23:44

Sora's 'stackable' library is old (quest has changed some of its scripting syntax, mainly List/Dictionary Attributes' scripting, from older versions), but I thought someone had updated it... (can't remember who did it or where it is found). It is quite advanced (maybe I could understand it now as I've been learning more in coding, but in the past, it was beyond my ability/understanding), but you can try to understand it, and update it yourself (if there's no updating of it by someone else and/or just can't find it, though I'm pretty sure it was updated by someone) and/or create your own 'item stacking' system.

Also, on top of a 'item stacking' system, you also would want to have a 'storage' system too: you don't use the 'player' Player Object as your 'inventory', but rather a bunch of Objects, creating a storage system (literally using these Objects as like folders on your computer, to organize and hold your 'item' Objects, as many games do/have, hidden and unseen by the person playing the game, but you can access these storage Objects, do various actions with them: sorting, viewing the storage/list of the 'item' Objects, viewing the individual 'item' Objects: their stats/attributes/info/etc, moving/getting these 'items' out from the 'storage' Objects and into some other Object or the 'player' Player Object for the person playing the game to be able to see and directly do stuff with those 'item' Objects, moving/putting/storing your 'item' Objects into the correct 'storage' Objects of your 'storage' system, and whatever other actions I might be missing that you'd want to do able to do...


jmnevil54
07 Jun 2017, 00:19

There was a thread like this a while ago. I seem to have lost it.
What I did is turn certain items into a variable, in this case potions.

player.potion = 1
player.hyper_potion = 0
player.gold = 500
player.statusattributes = NewStringDictionary()
blah....
dictionary add (player.statusattributes, "potion", "Potions: !")
dictionary add (player.statusattributes, "hyper_potion", "Hyper Potions: !")
dictionary add (player.statusattributes, "gold", "Gold: !")

Edit: Here's the thread.
http://textadventures.co.uk/forum/quest/edit/etfvbtpxbkk_iakwvkridw/3400b7b0-976a-4a42-91ec-a298226d8fd4


hegemonkhan
07 Jun 2017, 01:07

for a quick incomplete example of this:

<object name="storage_object">
  <object name="sword_storage_object">
  </object>
  <object name="axe_storage_object">
  </object>
</object>

<object name="katana_object">
  <inherit name="sword_type" />
  <attr name="alias" type="string">katana</attr>
  <attr name="parent" type="object">player</attr> // for this example, it's currently/initially in your 'inventory' (currently/initially contained within your 'player' Player Object)
  <attr name="stat_info_script_attribute" type="script">
    ClearScreen
    // blah scripting to display all of your weapon's stats/info
    wait {
      ClearScreen
    }
  </attr>
</object>

<type name="sword_type">
</type>

<object name="battle_axe_object">
  <inherit name="axe_type" />
  <attr name="alias" type="string">battle axe</attr>
  <attr name="parent" type="object">player</attr> // for this example, it's currently/initially in your 'inventory' (currently/initially contained within your 'player' Player Object)
  <attr name="stat_info_script_attribute" type="script">
    ClearScreen
    // blah scripting to display all of your weapon's stats/info
    wait {
      ClearScreen
    }
  </attr>
</object>

<type name="axe_type">
</type>

<object name="player">
</object>

// this will always be an Object/button inside of the 'player' Player Object (due to disabled 'take and drop' and its 'parent' set to the 'player' Player Object) --- it's the same as if you had nested this inside of the 'player' Player Object (but I have it separated, as I don't like having this clutter up the 'player' Player Object and I like it to be separated for organization and it'seasier to edit it as well, in code):
<object name="storage_button_object">
  <attr name="alias" type="string">storage</attr>
  <attr name="parent" type="object">player</attr>
  <attr name="take" type="boolean">false</attr>
  <attr name="drop" type="boolean">false</attr>
  // since this Object is always contained within the 'player' Player Object, we don't need to handle the built-in 'displayverbs' Stringlist Attribute, but if you'd rather have this Object not be contained within the 'player' Player Object, such as having it be contained within (implying we're constantly moving/setting it to ---> ) the parent Object of the 'player' Player Object (aka the Room Object that the player is within/at), then we'd need to use the 'displayverbs' instead of the 'inventoryverbs'.
  <attr name="inventoryverbs" type="simplestringlist">deposit_script_attribute;withdrawal_script_attribute;audit_script_attribute</attr>
  <attr name="deposit_script_attribute" type="script">
    <![CDATA[
      msg ("Deposit which item?")
      inventory_objectlist_variable = ScopeInventory ()
      DisplayList (inventory_objectlist_variable, true) // or maybe it's: DisplayList (inventory_objectlist_variable, 0) // or maybe it's: DisplayList (inventory_objectlist_variable, 1) // whatever it needs to make it be a numbered list... meh (as can be seen, I'm not clear on exactly what it needs for its second Argument and I've not tried/tested.used it yet myself)
      get input (
        if (IsInt (result) and ToInt (result) > 0 and ToInt (result) <= ListCount (inventory_objectlist_variable)) {
          selected_inventory_object_variable = ObjectListItem (inventory_objectlist_variable, result) // or maybe it's (it's been awhile since I've done quest scripting / been some time since I've used quest, lol): selected_inventory_object_variable = ObjectListItem (inventory_objectlist_variable, ToInt (result))
          // you can make this more efficient (less work/typing for you) if you got a lot of types of items to check, but for this simple example of only having 2 types to check (sword vs axe), I'm doing it in this simple but inefficient way (ask if you want/need better handling/efficiency for having lots of types of items and/or whatever else for an advanced/complex design you want/got/need)
          if (DoesInherit (selected_inventory_object_variable, "sword_type")) {
            selected_inventory_object_variable.parent = sword_storage_object // or you can do this too: MoveObject (selected_inventory_object_variable, sword_storage_object)
          } else if (DoesInherit (selected_inventory_object_variable, "axe_type")) {
            selected_inventory_object_variable.parent = axe_storage_object // or you can do this too: MoveObject (selected_inventory_object_variable, axe_storage_object)
          } else {
            msg ("THIS IS JUST A SIMPLE INCOMPLETE EXAMPLE, SO NO HANDLING OF THIS ISSUE, ASIDE FROM THIS MESSAGE")
          }
        }
      }
    ]]>
  </attr>
  <attr name="withdrawal_script_attribute" type="script">
    // again, this is very simple/inefficient as it's just an example for you (ask and we can make this more efficient):
    msg ("Withdraw which item?")
    msg ("1. sword")
    msg ("2. axe")
    get input {
      if (result = "1") {
        show menu ("Which sword to withdraw?", GetDirectChildren (sword_storage_object), false) {
          result.parent = player // or: MoveObject (result, player)
        }
      } else if (result = "2") {
        show menu ("Which axe to withdraw?", GetDirectChildren (axe_storage_object), false) {
          result.parent = player // or: MoveObject (result, player)
        }
      }
    }
  </attr>
  <attr name="audit_script_attribute" type="script">
     // again, this is very simple/inefficient as it's just an example for you (ask and we can make this more efficient):
    msg ("Audit which storage?")
    msg ("1. sword")
    msg ("2. axe")
    get input {
      weapon_type_input_selection_string_variable = result
      if (weapon_type_input_selection_string_variable = "1") {
        sword_storage_objectlist_variable = GetDirectChildren (sword_storage_object)
        DisplayList (sword_storage_objectlist_variable, true) // or: DisplayList (sword_storage_objectlist_variable, 0) // or: DisplayList (sword_storage_objectlist_variable, 1) // or: DisplayList (sword_storage_objectlist_variable, false)
        ask ("Do you want to Audit a specific sword?") {
          ask_boolean_variable = result
          if (ask_boolean_variable) { 
            show menu ("Which sword to audit?", sword_storage_objectlist_variable, false) {
            selected_sword_object_variable = result
            do (selected_sword_object_variable, "stat_info_script_attribute")
          }
        }
      } else if (weapon_type_input_selection_string_variable = "2") {
        axe_storage_objectlist_variable = GetDirectChildren (axe_storage_object)
        DisplayList (axe_storage_objectlist_variable, true) // or: DisplayList (axe_storage_objectlist_variable, 0) // or: DisplayList (axe_storage_objectlist_variable, 1) // or: DisplayList (axe_storage_objectlist_variable, false)
        ask ("Do you want to Audit a specific axe?") {
          ask_boolean_variable = result
          if (ask_boolean_variable) { 
            show menu ("Which axe to audit?", axe_storage_objectlist_variable, false) {
            selected_axe_object_variable = result
            do (selected_axe_object_variable, "stat_info_script_attribute")
          }
        }
      }
    }
  </attr>
</object>

<verb name="deposit_script_attribute">
  <property>desposit</property>
  <pattern>deposit</pattern>
  <defaultexpression>You can't deposit that!</defaultexpression>
</verb>

<verb name="withdrawal_script_attribute">
  <property>withdrawal</property>
  <pattern>withdrawal</pattern>
  <defaultexpression>You can't withdraw that!</defaultexpression>
</verb>

<verb name="audit_script_attribute">
  <property>audit</property>
  <pattern>audit</pattern>
  <defaultexpression>You can't audit that!</defaultexpression>
</verb>

hegemonkhan
07 Jun 2017, 01:22

P.S.

and if you got/need/want a really advancd/complex system (you got lots of items and types of items):

then, you should do Data Structure Management designs (linked lists, dictionaries - not to be confused with quest's 'Dictionary' Attribute, maps/mapping, stacks/queries, pointer/reference iterator, etc etc etc), for efficiency, which I failed at... still trying to learn this stuff and pass the school class next time I take it, sighs.


The Pixie
07 Jun 2017, 09:07

I wrote a new version of Sora's library some time ago. I have just uploaded it, it should work better and (IMO) be easier to use.

https://github.com/ThePix/quest/wiki/StackLib


DarkBlueMonkey
09 Jun 2017, 12:57

Thank you so much for uploading a new version of the Sora library. I'll import it and see if it compiles with my game now.

I guess my trepidation to using user-libraries is for this exact reason, and partly the reason why I stopped playing Kerbal Space Program for a long time; Every time there's an update, any user-mods run the substantial risk of becoming broken. If the online app then breaks, that means your game won't work any more...

Considering the functionality added by Sora's (now The Pixie's?) library seems so fundamental to RPG games, I would have expected it to be added by now.

Thanks to the others for the suggestions about using variables etc, but things just started getting horrifically complicated. I have lots of buffs, ammo types, weapons, consumables, junk, etc, all sorts of stuff that the player can pick up. Having bespoke rules for each one, and essentially duplicate the rules engine of Quest for 30 to 40+ objects (minimum) became such an overwhelming task, that I gave up (eventually the Quest UI started having problems displaying the scripts!

Once again, thank you for updating and uploading Sora's library, however I'd like to extend a formal feature request to the devs to put this functionality as standard, so that it can never be 'deprecated' or 'break' in a future release :-)

Thanks all!


The Pixie
09 Jun 2017, 13:28

Once your game is published and uploaded, it should be immune to changes in Quest. The web player is certainly capable of player game created in Quest 4 and possibly earlier versions. And the way the publishing process works is that it builds a single file from all the library files, including the built-in ones, so if Quest is updated, your game will still work as it is still using the original.


DarkBlueMonkey
09 Jun 2017, 14:01

I just read your explanation on the Git site.. That's cool that it won't "die" when an update occurs... From reading the method, it does seem a lot simpler than Sora's... However in its current form the library helps a LOT, but doesn't do /quite/ what I need... I guess I need to look into doing some coding...

  • I don't really want te user to still have to pick which object from the stacks when doing something.. when I say "use battery", they're all clones, so whichever is 'top' I suppose :)

TBH at the moment, it's not quite working.... I made a room "Stack Objects", made a stack object "stack_batteries" and added the stackable thingummy to the battery object pointing at the "stack_batteries" object... but when I pick up the batteries, I'm getting two batteries still. :-( I'll keep playing with it! I tried with the stack being "stack_itself" and "not stackable"... :(


jmnevil54
10 Jun 2017, 04:04

I was going to post a link to a thread here, but it seems either someone deleted it or it got knocked back.
Oh well.
Nice to see you have made progress, anyways.


The Pixie
10 Jun 2017, 07:40

If you can wait a few days, I will see if I can update the library. An option on the stack object so if the player selects it, it automatically picks the first object in the stack, instead of asking the player would work, I guess.


The Pixie
12 Jun 2017, 07:53

I have uploaded a new version, 1.1, which supports homogeneous stack as well as heterogeneous (same link as above). I also added piles of cash! Let me know of any issues.


DarkBlueMonkey
12 Jun 2017, 10:17

Thanks! Will download later and have a play. I had a good bash at it last night, and created a couple of dozen stack objects for my weapons and ammo types. Works nicely in the latest Quest UI, thanks! I ended up getting it to work by editing the code in the .aslx. My (poor) understanding of it seemed to indicate that if I just commented out the "If there's more than one" check, then it would always assume there was one, and would take the first one :) . as soon as I did that, the battery got used, and the count decremented exactly! :) I suspect your solution will be far neater, so will have a look at that! :)

Tonight's puzzle: At the moment I have to say "get battery" then "use batteries" because the stack's called "batteries", so I might try to tweak the code a little to show an alternate description (listdescription?) in the inventory pane, so that the stack can respond to "use battery", but still show "batteries: 2" ... might have to create a special verb other than use, so that the stack and floor objects don't pop up in the same "use which?" list, but custom verbs are a tad beyond my skill level just now :D I'm completely shooting in the dark here, and (to be honest) was a little shocked when Quest parsed my hacked-about stack-library .aslx :D

Thank you so much for updating this library, and your help. It's completely un-jammed me from a problem that stalled for months cos my quest coding skill wasn't up to it :)


JenniferKline
30 Jun 2017, 19:05

Just trying out the stackable items, not sure if it's working for me.

Got an item, Drink and an item Banana Thickshake. Both of them are set to be members of the stack Potion Stack, which is locked in a room and marked as a heterogenous stack.

Inventory still seems to treat them as normal. I've noticed that the was something that happened before and filled the inventory tab with scripts. Seems to happen now when I make a new item , but I get the error

Error running script: Error compiling expression 'container.parent = Stack Containers': Unknown object or variable 'Stack Containers'


The Pixie
30 Jun 2017, 20:01

I realise the documentation is wrong. You need to put all the stack containers (i.e., Potion Stack) in a room called "Stack Containers".


scrimshaw04
04 Jul 2017, 10:20

A couple of questions...

Firstly I'm trying to create an encumbrance system that totals the weight of all the objects in a player's inventory. Is there a way to retrieve how many items there are in a stack as an integer? Or reference an attribute of all of the items in that stack?

Secondly, how can I get this system to work with clones? For example, say I had a fountain in my game that the player can retrieve water from. The way that I'm currently attempting to do this is to have a water object and a water stack object. When the player retrieves some water from the fountain, it clones the water object to the player's inventory. At the moment this just adds more copies of the water object to the player's inventory. What I'd like to be able to do is add the stack to the player's inventory, then just increase the count of objects in that stack (i.e. Water: 1, Water: 2 etc...). Is there any way to do this?


The Pixie
04 Jul 2017, 10:41

If your stack container is called "Potion Stack", then GetDirectChildren(Potion Stack) will get a list of items in the stack, and ListCount(GetDirectChildren(Potion Stack)) will get the number of items in the stack.

To total the weights, try (might be volume rather than weight?):

total = 0
foreach (o, GetDirectChildren(Potion Stack)) {
  total = total + o.weight
}

hegemonkhan
04 Jul 2017, 18:51

(if you want/need to do so) how to store the Value from Pixie's example of 'ListCount'

give your container stack an Integer Attribute, for example name: 'quantity'

Potion Stack.quantity = ListCount (GetDirectChildren (Potion Stack))


also note that you can use the 'foreach' iteration Pixie showed, for additional layers (up or down)

for example, you can have a potion container stack for all of the different types of individual potion containers:

potion_stack
-> life_potion_stack
->-> life_potion_1
->-> life_potion_2
-> mana_potion_stack
->-> mana_potion_1
->-> mana_potion_2

elixir_stack
-> strength_elixir_stack
->-> strength_elixir_1
->-> strength_elixir_2
-> endurance_elixir_stack
->-> endurance_elixir_1
->-> endurance_elixir_2


but this (if using lots of data/variables/objects/items/etc) gets into performance/speed of code designs (analyzing code designs: steps/operations/combinations and thus its speed/performance):

https://en.wikipedia.org/wiki/Computational_complexity_theory
https://en.wikipedia.org/wiki/Time_complexity#Table_of_common_time_complexities

https://en.wikipedia.org/wiki/Data_structure
https://en.wikipedia.org/wiki/Abstract_data_type
https://en.wikipedia.org/wiki/List_of_data_structures


I had a hard time with this college programming class, totally failed it... sighs, hopefully I can learn this stuff and pass it next time. Not easy stuff for your brain, as it's dealing with structures/designs, something, at least my brain wasn't capable/ready for, sighs.


The Pixie
04 Jul 2017, 19:22

(if you want/need to do so) how to store the Value from Pixie's example of 'ListCount'

give your container stack an Integer Attribute, for example name: 'quantity'

I would actually advise against that. Better to get the value with ListCount whenyou need it, because you can be confident that is right, rather than rely on an attribute getting updated everytime. This is why Quest does not have a list of inventory items as an attribute of the player - and in fact very rarely (if ever) uses object lists as attributes.


hegemonkhan
05 Jul 2017, 01:00

true, good points, I never would have, thought-of or realized, them.

As Pixie pointed out:

if you were to decide to store the quantity count into an Attribute of your container Object, you got to know what you're doing, as there's a lot of issues with it, if you're not careful.


Marzseaz
05 Jul 2017, 09:33

I'm using a second inventory in my game. By default the stacks always appear in the player's primary inventory, but sometimes (not always) I would like the stacks to appear in the player's second inventory instead. Is there any way to determine which stacks go where?


JenniferKline
05 Jul 2017, 10:48

"Secondly, how can I get this system to work with clones? For example, say I had a fountain in my game that the player can retrieve water from. The way that I'm currently attempting to do this is to have a water object and a water stack object. When the player retrieves some water from the fountain, it clones the water object to the player's inventory. At the moment this just adds more copies of the water object to the player's inventory. What I'd like to be able to do is add the stack to the player's inventory, then just increase the count of objects in that stack (i.e. Water: 1, Water: 2 etc...). Is there any way to do this?"

Personally, I'd make a clone, I'm pretty sure they'd stack, but I'm not totally sure myself how m to put them in the inventory straight away. I'd put Water in a safe room the player can't get to. Then I'd have a turn script to check if there is Water in the fountain room. If no, then clone water and move to fountain room, so it's an object the player can pick up constantly. Clones are stackable, so. I think there'd probably be better ways to do this.


scrimshaw04
08 Jul 2017, 14:14

Hmm okay, I'll see if I can get that method to work. Thanks for the help everyone!


onimike
17 Jul 2017, 02:46

Kinda what Jennifer was saying use a clone, but pick up actual item and increase the items volume by +1. Then when you use item -1 from the items volume then. So this is what you do, first add script to on enter room, clone and add heath pack to room. Then on the item health pack add this script to inventory on Take

if (Got(health)) {
  IncreaseObjectCounter (health, "volume")
  RemoveObject (this)
}
else if (not Got(health)) {
    RemoveObject (this)
    AddToInventory (health)
    IncreaseObjectCounter (health, "volume")
  }


and add this script to on Drop

if (Got(health)) {
  if (GetInt(health, "volume") >= 2) {
    DecreaseObjectCounter (health, "volume")
    CloneObjectAndMove (health, player.parent)
  }
  else if (GetInt(health, "volume") = 1) {
    DecreaseObjectCounter (health, "volume")
    MoveObject (health, Items*)
    CloneObjectAndMove (health, player.parent)
  }
}

*Pay attention to this by items, this is a room i made to hold items when their not in play.

This should give you the results without having to do much. Also to show how many you have of something either make a verb or add this to the Look At script as a print expression

"You have  " +health.volume+ "health packs available" 

The +health.volume+ will print the number that is in stack so you know how many you have.


onimike
17 Jul 2017, 12:08

Marzseaz wrote:
I'm using a second inventory in my game. By default the stacks always appear in the player's primary inventory, but sometimes (not always) I would like the stacks to appear in the player's second inventory instead. Is there any way to determine which stacks go where?

To use the second inventory pane you have to set the attribute manually every time. So best option to use that pane is use it for magic or skills so you only need to update it when you gain a new skill, but remember you have to also add any old skills that you had on there before or they will disappear from the second inv pane.