Hiding a status attribute?

CheeseMyBaby
07 May 2018, 14:06

Let's say I wanted the "Health" status hidden unless I have a certain object in my hands/inventory

Is it possible to hide a status attribute in the status pane?
And showing it again when the player is holding/carrying certain object?

To try to examplify it:

Status pane is empty.
Player picks up a box of taco's.
Status pane contains "Tacos !/5"

(bad example but I'm hungry and could really use a taco right now)


hegemonkhan
07 May 2018, 14:26

the built-in 'statusattributes' Dictionary Attribute can have its items added/removed:

http://docs.textadventures.co.uk/quest/using_dictionaries.html

http://textadventures.co.uk/forum/samples/topic/5137/list-and-dictionary-extensive-guide-by-hk

http://textadventures.co.uk/forum/samples/topic/5138/explore-and-travel-code-sample-by-hk (doesn't use the 'statusattributes', but you can see/study it, as it involves adding/removing items from a dictionary/list, if you need help understanding it, let me know, as it is a bit more advanced coding design, using lists and especially dictionaries)


player.Health = 999

player.statusattributes = NewStringDictionary ()

dictionary add (player.statusattributes, "Health", "Health: " + !) // not sure on its syntax, as I usually don't script-in (add) the dictionary items... let me know if it doesn't work...

dictionary remove (player.statusattributes, "Health")

------------

// as a creation tag, this works (if I don't have any mistakes/typos/etc, lol):

<object name="room">

  <inherit name="editor_room" />

</object>

<object name="player">

  <inherit name="editor_object" />
  <inherit name="editor_player" />

  <attr name="parent" type="object">room</attr>

  <attr name="Health" type="int">999</attr>

  <statusattributes type="stringdictionary">

    <item>
      <key>Health</key>
      <value>Health: !</value>
    </item>

  </statusattributes>

</object>

http://textadventures.co.uk/forum/samples/topic/5559/attributes-and-if-script-guide-by-hk (Attributes and the 'if' Script usage)

http://textadventures.co.uk/forum/quest/topic/5387/i-really-need-help#37375 (if you need help with 'statusattributes' usage)

ask if you need help or got any questions


CheeseMyBaby
07 May 2018, 20:39

Oh dear.
Dictionaries.
I was hoping I could wait a year or 50 before having to try to wrap my head around those things.
Oh dear.

Thank you though HK. I'll see what I can do.
I might end up not doing this xD


CheeseMyBaby
07 May 2018, 20:47

I'll try to be more specific and, hopefully, it's doable without dictionaries?

When starting this act the status pane looks like this:

HEALTH: 100%
ARMOR: 0
CARRYING: 0/KG
AMMO: 7/7

The thing is, I don't want the AMMO: 7/7- part to show up in the pane until the player actually picks up the pistol.





And, of course, I'd like the AMMO:to disappear if/when the player drop the pistol.

So, if the player do this:

  1. Pick up pistol
  2. Fire two rounds
  3. Drop pistol
  4. Pick up pistol

I want the ammo-part in the status pane to.

  1. Be shown with 7/7 rounds in clip
  2. Be shown with 5/7 rounds in clip
  3. Disappear
  4. Be shown with 5/7 rounds in clip

Is the use of dictionaries still the way to go?


Dcoder
07 May 2018, 21:20

Dictionaries are very similar to lists, having both a key plus its corresponding value, rather than just a single string/object.

Removing the dictionary key is simple (dictionary you want to remove from, dictionary key):
dictionary remove (player.statusattributes, "TacoKey")

Adding the key (plus its value) is simple too (dictionary to add to, dictionary key, key value):
dictionary add (player.statusattributes, "TacoKey", "Tacos: !/5")

The only tricky part is keeping the dictionary in the order that you want, since when you add the TacoKey back, it will be added to the bottom of the dictionary. To fix that, instead of simply adding back TacoKey, you will recreate the player.statusattributes dictionary from scratch and add back all the original keys plus the "Tacokey", but in the proper order:
player.statusattributes = NewStringDictionary ()
dictionary add (player.statusattributes, "YourFirstKey", "YourFirstValue")
dictionary add (player.statusattributes, "YourSecondKey", "YourSecondValue")
etc., etc.

You can then call function UpdateStatusAttributesif the status pane doesn't update the new dictionary fast enough. Oh, and I would put all of the dictionary reconstruction part of this in a function so that you easily repeat this.


K.V.
07 May 2018, 21:28

Example game:

<!--Saved by Quest 5.7.6606.27193-->
<asl version="550">
  <include ref="English.aslx" />
  <include ref="Core.aslx" />
  <game name="ShowMenu Mayhem">
    <gameid>cacc93d2-827f-4d01-8ad1-608dee20a069</gameid>
    <version>1.0</version>
    <firstpublished>2018</firstpublished>
    <showhealth />
    <showscore />
    <showmoney />
    <start type="script">
      dictionary remove (game.pov.statusattributes, "ammo")
    </start>
  </game>
  <object name="room">
    <inherit name="editor_room" />
    <beforeenter type="script">
    </beforeenter>
    <enter type="script">
    </enter>
    <object name="player">
      <inherit name="editor_object" />
      <inherit name="editor_player" />
      <ammo type="int">0</ammo>
      <statusattributes type="stringdictionary">
        <item>
          <key>ammo</key>
          <value></value>
        </item>
      </statusattributes>
    </object>
    <object name="taco">
      <inherit name="editor_object" />
      <take />
      <ontake type="script">
        IncreaseObjectCounter (game.pov, "ammo")
      </ontake>
      <ondrop type="script">
        DecreaseObjectCounter (game.pov, "ammo")
      </ondrop>
    </object>
  </object>
  <turnscript name="toggle_ammo_stat_att">
    <enabled />
    <script><![CDATA[
      if (game.pov.ammo < 1) {
        if (DictionaryContains(game.pov.statusattributes, "ammo")) {
          dictionary remove (game.pov.statusattributes, "ammo")
        }
      }
      else {
        if (not DictionaryContains(game.pov.statusattributes, "ammo")) {
          dictionary add (game.pov.statusattributes, "ammo", "Ammo: !")
        }
      }
    ]]></script>
  </turnscript>
</asl>

EDIT

Ah! Dcoder posted while I was testing. Everything he says is true, even though that's not exactly how I'm handling things in my example.


Dcoder
07 May 2018, 22:13

Just thought of a problem -- if your player.statusattributes dictionary has 2 or more keys removed, and you want to reconstruct it but only want to add back the 1 key (like TacoKey). Then you'll have to create a new function, with the key that you want to add back to be the only parameter:

In this function, a second (temporary) string dictionary like player.Tempstatusattributes is created. Then use if/then scripts to add to it each key/value in proper order -- if the key IS the parameter (the key that you want to add back) OR already exists in the original player.statusattributes, then add it to the new player.Tempstatusattributes. This way, keys that have been previously removed from the original player.statusattributes (and that you want to keep removed) will not be added to the new player.Tempstatusattributes. When finished with the new dictionary, make player.statusattributes = player.Tempstatusattributes and then UpdateStatusAttributes.

If all this sounds confusing, it is : ) I can explain things more thoroughly if you want, step by step. It's actually simple in concept, just difficult to describe in words.


Dcoder
07 May 2018, 22:37

When constructing the new dictionary player.Tempstatusattributes, put this in your new function for each possible key, in order:

if (player.Health = parameter or DictionaryContains(player.statusattributes, "player.Health")) {
  dictionary add (player.Tempstatusattributes, "player.Health", "HEALTH: !")
}
if (player.Armor = parameter or DictionaryContains(player.statusattributes, "player.Armor")) {
  dictionary add (player.Tempstatusattributes, "player.Armor", "ARMOR: !")
}

etc., etc.

When finished, player.Tempstatusattributes should contain all of the keys/values that are currently in player.statusattributes, plus the key/value that you are adding back (through the parameter), but NOT any of the previously removed keys, and all in the correct order.


Dcoder
07 May 2018, 23:01

Ok, so I created a function RedoStatusPane (or whatever) with the parameter ReaddedKey. The function looks like this:

player.Tempstatusattributes = NewStringDictionary()
if (player.health = ReaddedKey or DictionaryContains(player.statusattributes, "player.health")) {
  dictionary add (player.Tempstatusattributes, "player.health", "HEALTH: !")
}
if (player.armor = ReaddedKey or DictionaryContains(player.statusattributes, "player.armor")) {
  dictionary add (player.Tempstatusattributes, "player.armor", "ARMOR: !")
}
if (player.carrying = ReaddedKey or DictionaryContains(player.statusattributes, "player.carrying")) {
  dictionary add (player.Tempstatusattributes, "player.carrying", "CARRYING: ! kg")
}
if (player.ammo = ReaddedKey or DictionaryContains(player.statusattributes, "player.ammo")) {
  dictionary add (player.Tempstatusattributes, "player.ammo", "AMMO: !/7")
}
player.statusattributes = player.Tempstatusattributes
UpdateStatusAttributes

So whenever you want to add a key back to the status pane, call this function with the key as the sole parameter. This is untested, so please complain if I made any errors : )


Dcoder
07 May 2018, 23:16

Or you could do like KV's example, where he has a turn script updating the status pane every turn (instead of my manually-called function).


mrangel
07 May 2018, 23:42

An alternative way might be overriding this function:

  <function name="FormatStatusAttribute" parameters="attr, value, format" type="string">
    if (HasAttribute (game, "hidestatusattributes")) {
      if (ListContains (game.hidestatusattributes, attr)) {
        return ("")
      }
    }
    if (LengthOf(format) = 0) {
      return (CapFirst(attr) + ": " + value)
    } else {
      if (TypeOf(value) = "int" and attr = "money" and HasString(game, "moneyformat")) {
        value = DisplayMoney(value)
      }
      else if (TypeOf(value) = "int" or TypeOf(value) = "double") {
        value = ToString(value)
      }
      else if (TypeOf(value) = "null") {
        value = ""
      }
      return (Replace(format, "!", value))
    }
  </function>

Then you can create a list attribute, game.hidestatusattributes, containing the names of attributes you don't want to display. Means you're saved the hassle of having to put the dictionaries back in the right order.

OR...

  <function name="UpdateStatusAttributes">
    <![CDATA[
    status = AddStatusAttributesForElement("", game, game.statusattributes)
    status = AddStatusAttributesForElement(status, game.pov, game.povstatusattributes)
    foreach (obj, ScopeInventory()) {
      if (HasAttribute (obj, "statusattributes")) {
        if (GetBoolean (obj, "worn") or GetBoolean (obj, "equipped")) {
          status = AddStatusAttributesForElement(status, obj, obj.statusattributes)
        }
      }
    }
    status = AddStatusAttributesForElement(status, game.pov, game.pov.statusattributes)
    request (SetStatus, status)
    ]]>
  </function>

This would allow you to give any object a statusattributes attribute; specifying attributes which are added to the status pane whenever the player is wearing that item. (statusattributes is still a dictionary, but it doesn't need editing during the game. You can set it up just like you do the main statusattributes, except it will be on the pistol object rather than the game or player)

(I assume you are able to set a boolean attribute "equipped" for an equipped pistol; or use the "wear" system to handle weapons; or just set "equipped" to be always true if you want the attribute to appear as soon as you pick it up)

... kind of tempted to make the 'worn' test into if (GetBoolean (obj, "worn") = HasBoolean (obj, "worn") and GetBoolean (obj, "equipped") = HasBoolean (obj, "equipped")) { ... maybe easier to deal with, but maybe makes the code less human-readable.
Would basically mean "if this item is wearable or equippable, only show its status attributes when it's worn or equipped."


hegemonkhan
08 May 2018, 04:57

(filler for getting my edited post, updated/posted)


@ CheeseMyBaby:

hopefully this post will help you understand dictionaries (and lists) very well... lol


both lists and dictionaries are essentially 'conversions' or 'input-output' functionalities, to hopefully help you understand them or understand them better, lol


LISTS

input (index number: 0 to ListCount - 1) ---> output (value attached to that index number)

example_stringlist_variable = NewStringList ()
list add (example_stringlist_variable, "red")
list add (example_stringlist_variable, "blue")
list add (example_stringlist_variable, "yellow")

ListCount (and DictionaryCount for dictionaries) is the quantity of items in the list (or dictionary for dictionaries), so remember this difference between ListCount (quantity of items) and the index numbering (starts at 0, not 1, so the last item's index number is: 1 less than the ListCount~quantity-of-items)

a list item: input (an index number: 0 to ListCount - 1) and output (value: a String if using a String List or an Object if using an Object List)

'red', being the first item added to our list, is given the input of index number '0', and its string value (as I made/am-using a String List) of "red", example: String List Item: input (0) = output ("red")

'blue', being the second item added to our list, is given the input of index number '1', and its string value (as I made/am-using) a String List) of "blue", example: String List Item: input (1) = output ("blue")

'yellow', being the third (and last in this case/example) item added to our list, is given the input of index number '2' (ListCount of 3 items - 1 = last item's index number), and its string value (as I made/am-using) a String List) of "yellow", example: String List Item: input (2) = output ("yellow")

you do NOT get any control over the inputs for lists (the index number inputs are always ordered from '0' to 'ListCount-1', by their added order to the list and/or by the order within the list if you've removed items)

OUTPUT = StringListItem (STRING_LIST, INPUT)
STRING_VALUE = StringListItem (STRING_LIST, index number)

OUTPUT = ObjectListItem (OBJECT_LIST, INPUT)
OBJECT_VALUE = StringListItem (OBJECT_LIST, index number)

string_variable = StringListItem (example_stringlist_variable, 0)
msg (string_variable)
// displayed output: red

string_variable = StringListItem (example_stringlist_variable, 1)
msg (string_variable)
// displayed output: blue

string_variable = StringListItem (example_stringlist_variable, 2)
msg (string_variable)
// displayed output: yellow

string_variable = StringListItem (example_stringlist_variable, 3)
msg (string_variable)
// displayed output: ERROR (there is NO 4th item in the 'example_stringlist_variable' String List)


DICTIONARIES

unlike lists, dictionaries use custom strings (you have full control, deciding what to name your string inputs attached to each of your values, of each item you want to add to your dictionary) for their inputs, so there's NO index numbers at all with dictionaries (unless you make/have your string inputs be numbers, lol, though its still not behaving the same underneath, but it has the same effect in its usage)

dictionaries do still have a DictionaryCount (as it does still have items, and thus knowing the quantity of items, is still useful, lol), but again, there's no index numbers as inputs, but rather custom strings of your own choosing (and again, these custom strings, can be numbers, so it can act like lists and their index numbers, though technically it's not the same thing going on underneath)

input (STRING) ---> output (Value attached to the STRING INPUT)

String Dictionary's Item: input key (STRING) = output value (STRING)
Object Dictionary's Item: input key (STRING) = output value (OBJECT)
SCRIPT Dictionary's Item: input key (STRING) = output value (SCRIPT)


while the input string and output value are attached together, it's only one way: input to output

for a String Dictionary ONLY: if you want to go from 'output to input', then you add another item, with the output STRING now as the input STRING, and the input STRING now as the output STRING

for example:

Item 1: input ("fire") = output ("water")
Item 2: input ("water") = output ("fire")


example_stringdictionary_variable = NewStringDictionary ()
list add (example_stringdictionary_variable, "fire", "water")
list add (example_stringdictionary_variable, "water", "fire")
list add (example_stringdictionary_variable, "earth", "air")
list add (example_stringdictionary_variable, "air", "earth")

// input ("fire") ---> output ("water")
// input ("water") ---> output ("fire")
// input ("earth") ---> output ("air")
// input ("air") ---> output ("earth")

string_variable = StringDictionaryItem (example_stringdictionary_variable, "fire")
msg (string_variable)
// display output: water

string_variable = StringDictionaryItem (example_stringdictionary_variable, "water")
msg (string_variable)
// display output: fire

string_variable = StringDictionaryItem (example_stringdictionary_variable, "earth")
msg (string_variable)
// display output: air

string_variable = StringDictionaryItem (example_stringdictionary_variable, "air")
msg (string_variable)
// display output: earth


dictionaries have a lot of really cool applications...

the example I used was opposing magic (as this should be a familar concept/application if you've ever played a rpg or whatever type of game with elemental magic damage types/modifiers)... which can be used for doing more damage ("weak" to elemental magic type) or less damage ("strong" to elemental magic type) or normal/abosrb/reflect damage functionality

but there's tons of other really cool/useful applications with dictionaries (and lists+dictionaries used together too)


hegemonkhan
08 May 2018, 05:33

@ DCoder:

sorting (dictionaries and lists) is Data Structure+Management code design: trees, linked lists, mapping, dictionaries, iterators, etc etc etc

https://en.wikipedia.org/wiki/Tree_(data_structure)
https://www.cs.cmu.edu/~adamchik/15-121/lectures/Linked%20Lists/linked%20lists.html

etc etc etc links of all the other code stuff (mapping, dictionaries, iterators, etc et cetc)


CheeseMyBaby
08 May 2018, 07:31

Wow, you guys!
I just arrived at work with a slight fever and too much to do. I'll try to read this through tonight but it sure looks like I'll be able to understand it using all your different kinds of explanation!
Seriously... thanks so much!!

(It's difficult for me to learn by 'just' reading. Ideal would be to have something sit next to me, talking, explaining and doing. This is why it took me 38 years to even start trying :))


Dcoder
08 May 2018, 11:52

Ha! Hey Cheese, aren't you glad you asked? :D

Hmm, maybe for Quest 5.9 there could be a built-in function to alphabetize or numeralize lists/dictionaries automatically. Then you could just name your strings starting with the appropriate letter/number, and the list/dictionary would easily arrange itself in the desired order.


hegemonkhan
08 May 2018, 12:18

well.. there's already an internal sorting mechanism (has anyone found this within the core code, or is it deeper within the quest engine or whatever programming itself?)... which annoys everyone, as they find their stuff re-organized around, from how they had it ordered in code or the GUI/Editor... lol


but ya, that's something quest still needs, lol

(1) sorting (Data Structure/Management code library or quest update)

(2) and also: input error and exception handling library or quest update, too...

these are both on my (far future / my procrastinating: "I will eventually"... which usually means: never, lol) "to do" lists... lol


CheeseMyBaby
08 May 2018, 12:33

I'm super glad I asked!!
I made it work. I tried all of the stuff you guys mentioned above and every single thing worked.
I still need to read and try to learn more but yes, I'm making headway (100% because of help from the likes of you guys. Thanks!!)