How to randomly run script from list, then remove

michaelsahn
Hello again -

Last week I asked how to randomly select, then remove an integer from a list.

Jaynabonne provided this elegant code to do so:

jaynabonne wrote:Here's a simple bit of code that does (I hope) what you want. You didn't say what you wanted to happen when all the strings were exhausted, so I added a small amount of logic to spit out a default message.

<!--Saved by Quest 5.4.4873.16527-->
<asl version="540">
<include ref="English.aslx" />
<include ref="Core.aslx" />
<game name="RandomSpeaktoTest">
<gameid>14736beb-f775-4ecd-b503-5f46568e0eee</gameid>
<version>1.0</version>
<firstpublished>2014</firstpublished>
</game>
<object name="room">
<inherit name="editor_room" />
<object name="player">
<inherit name="editor_object" />
<inherit name="editor_player" />
</object>
<object name="Chatty">
<inherit name="editor_object" />
<inherit name="male" />
<speak type="script">
count = ListCount(this.responses)
if (count = 0) {
s = "I have nothing left to say."
} else {
s = StringListItem(this.responses, GetRandomInt(0, count-1))
list remove(this.responses, s)
}
msg(s)
</speak>
<responses type="stringlist">
<value>I can't even remember the last time I saw someone.,</value>
<value>What can I do for you?</value>
<value>They say it's supposed to rain later. Be sure to take your umbrella,</value>
<value>Be careful what you say. Remember - the walls have teeth.</value>
<value>Argue for your limitations and, sure enough, they're yours.</value>
</responses>
</object>
</object>
</asl>


It's also attached as a file. The heart of it is this:
       count = ListCount(this.responses)
if (count = 0) {
s = "I have nothing left to say."
} else {
s = StringListItem(this.responses, GetRandomInt(0, count-1))
list remove(this.responses, s)
}
msg(s)

It selects a random string from the list and then removes the string from the list. The list is attached to the character in question. I assume each character will have its own response. Straightforward, I hope! :)


It was very useful - thanks again!

Jaynabonne's code was designed to randomly select and display text (as I had requested). Now I'd like modify it to run a script.

I'd like to use the provided code to randomly select and run a script from a list, then remove that script from the list.

I have attempted to do this on my own but have ended up writing long "else if" scripts, and I am hoping there a simpler, more efficient way of doing this.

Thanks again in advance for your help!

Mike

HegemonKhan
Jay's code already does this:

list remove(this.responses, s)

       count = ListCount(this.responses)
if (count = 0) {
s = "I have nothing left to say."
} else {
s = StringListItem(this.responses, GetRandomInt(0, count-1))
list remove(this.responses, s)
}
msg(s)


is Jay's code not working (not running the script) ???
if it's not, then just change the "msg (s)" to being between the "s = StringListItem(this.responses, GetRandomInt(0, count-1))" and "list remove(this.responses, s)"

michaelsahn
Instead of display text, I'd like to use the code to randomly select and run a script from a list, then remove that script from the list.

I've tried to set this up but don't know how to do it. Thanks.

HegemonKhan
ah, okay, my bad.

then just change the "msg (s)" to this:

"invoke (s)" ( http://quest5.net/wiki/Invoke )

invoke (StringListItem (StringList, Index_Number))

and to be safe, I moved the "invoke" location and applied the "on ready", lol

count = ListCount(this.responses)
if (count = 0) {
s = "I have nothing left to say."
} else {
s = StringListItem(this.responses, GetRandomInt(0, count-1))
invoke (s)
on ready {
list remove(this.responses, s)
}
}

michaelsahn
Thank you! And can I write scripts as string list items?

HegemonKhan
actually, I am messing up, argh, sorry.

you got to use~make a "Script Dictionary", for scripts

http://quest5.net/wiki/Using_Dictionaries
http://quest5.net/wiki/Scriptdictionary
http://quest5.net/wiki/ScriptDictionaryItem

and also use "invoke" or "do":

http://quest5.net/wiki/Invoke
http://quest5.net/wiki/Do

Script Dictionary:

Key (String) = Expression (Script)
string_1 = script_1
string_2 = script_2

conceptual: if input is string_1, then output is script_1

Scripting: invoke (ScriptDictionaryItem (Object.ScriptDictionary, String_Value)

for example:

Object.ScriptDictionary:
HK = run_laps
Michael = do_push_ups

invoke (Object.ScriptDictionary, HK)
output effect: HK will run laps

invoke (Object.ScriptDictionary, Michael)
output effect: Michael will do push ups

here's an example:

<object name="global_data_object">
<inherit name="editor_object" />
<attr name="homeland_events_script_dictionary" type="scriptdictionary">
<item key="grassland_discovery">
msg ("You've discovered the grassland! Now, you can travel to the grassland and explore it!")
</item>
<item key="plains_discovery">
msg ("You've discovered the plains! Now, you can travel to the plains and explore it!")
</item>
</attr>
</object>

<function name="explore_function">
invoke (ScriptDictionaryItem (global_data_object.homeland_events_script_dictionary, grassland_discovery))
// or: invoke (ScriptDictionaryItem (global_data_object.homeland_events_script_dictionary, plains_discovery))
</function>


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

here's an example game code of all of this stuff working together to do something ("explore" and "travel" features) in a game:

using "StringLists" and "GetRandomInt" and "ScriptDictionaries" together.

you "explore" to randomly select events in your starting area, some (well, I haven't added in other unrelated events, such as finding~selecting an item or equipment or a gold bag or a monster~fight event, as this is just for testing) of the events are "area discovery" events, which enable you to now (using the) "travel" to those areas. I don't want these events to get selected again when you "Explore", so they must be removed from the StringList (which thus then also prevents the ScriptDictionary of those events' scripts from running as well).

for example:

"explore" (ie: event) button
"travel" (ie: goto) button

Areas of the game:

grassland (starting area)
plains
desert
swamp

Grassland's "Explore" events:

1. discover the "plains" (allows you to now travel to the plains area)
2. discover the "desert" (allows you to now travel to the plains area)
3. discover the "swamp" (allows you to now travel to the plains area)
4. encounter an "orc" monster
5. find a 50 gold bag
6. get sick (lose 10 HP each turn until cured or dead)
7. etc...

plains' "explore" events:

1. etc etc etc
2. etc etc etc
etc etc etc

(not as elegant as Jay's coding, but it's functional, lol)

<asl version="540">
<include ref="English.aslx" />
<include ref="Core.aslx" />
<game name="Testing Game Stuff">
<gameid>eef801a1-4e6b-4b0a-bdbf-8f3ecfa8389c</gameid>
<version>1.0</version>
<firstpublished>2013</firstpublished>
<turns type="int">0</turns>
<statusattributes type="simplestringdictionary">turns=</statusattributes>
<start type="script">
msg ("Important Note:")
msg ("Type in: help")
</start>
</game>
<object name="homeland">
<inherit name="editor_room" />
<object name="player">
<inherit name="editor_object" />
<inherit name="editor_player" />
</object>
</object>
<object name="grassland">
<inherit name="editor_room" />
</object>
<object name="plains">
<inherit name="editor_room" />
</object>
<object name="desert">
<inherit name="editor_room" />
</object>
<object name="tundra">
<inherit name="editor_room" />
</object>
<object name="swampland">
<inherit name="editor_room" />
</object>
<object name="mountains">
<inherit name="editor_room" />
</object>
<object name="forest">
<inherit name="editor_room" />
</object>
<object name="wasteland">
<inherit name="editor_room" />
</object>
<object name="coastland">
<inherit name="editor_room" />
</object>
<object name="hills">
<inherit name="editor_room" />
</object>
<command name="help_command">
<pattern>help</pattern>
<script>
help_function
</script>
</command>
<command name="explore_command">
<pattern>explore</pattern>
<script>
explore_function
</script>
</command>
<command name="travel_command">
<pattern>travel</pattern>
<script>
travel_function
</script>
</command>
<object name="data_object">
<inherit name="editor_object" />
<travel_string_list type="simplestringlist">homeland</travel_string_list>
<homeland_events_string_list type="simplestringlist">grassland_discovery;plains_discovery;desert_discovery;tundra_discovery;swampland_discovery;forest_discovery;mountains_discovery;hills_discovery;wasteland_discovery;coastland_discovery</homeland_events_string_list>
<homeland_events_script_dictionary type="scriptdictionary">
<item key="grassland_discovery">
list add (data_object.travel_string_list, "grassland")
msg ("You've discovered the grassland! Now, you can travel to the grassland and explore it!")
</item>
<item key="plains_discovery">
list add (data_object.travel_string_list, "plains")
msg ("You've discovered the plains! Now, you can travel to the plains and explore it!")
</item>
<item key="desert_discovery">
list add (data_object.travel_string_list, "desert")
msg ("You've discovered the desert! Now, you can travel to the desert and explore it!")
</item>
<item key="tundra_discovery">
list add (data_object.travel_string_list, "tundra")
msg ("You've discovered the tundra! Now, you can travel to the tundra and explore it!")
</item>
<item key="swampland_discovery">
list add (data_object.travel_string_list, "swampland")
msg ("You've discovered the swampland! Now, you can travel to the swampland and explore it!")
</item>
<item key="forest_discovery">
list add (data_object.travel_string_list, "forest")
msg ("You've discovered the forest! Now, you can travel to the forest and explore it!")
</item>
<item key="mountains_discovery">
list add (data_object.travel_string_list, "mountains")
msg ("You've discovered the mountains! Now, you can travel to the mountains and explore it!")
</item>
<item key="hills_discovery">
list add (data_object.travel_string_list, "hills")
msg ("You've discovered the hills! Now, you can travel to the hills and explore it!")
</item>
<item key="wasteland_discovery">
list add (data_object.travel_string_list, "wasteland")
msg ("You've discovered the wasteland! Now, you can travel to the wasteland and explore it!")
</item>
<item key="coastland_discovery">
list add (data_object.travel_string_list, "coastland")
msg ("You've discovered the coastland! Now, you can travel to the coastland and explore it!")
</item>
</homeland_events_script_dictionary>
</object>
<turnscript name="global_turnscript">
<enabled />
<script>
game.turns = game.turns + 1
</script>
</turnscript>
<function name="help_function">
msg ("Type 'explore' to explore your area.")
msg ("Type 'travel' to travel to different areas.")
</function>
<function name="explore_function"><![CDATA[
switch (game.pov.parent) {
case (homeland) {
result_1 = ListCount (data_object.homeland_events_string_list) - 1
if (result_1 >= 0) {
result_2 = StringListItem (data_object.homeland_events_string_list,GetRandomInt(0,result_1))
invoke (ScriptDictionaryItem (data_object.homeland_events_script_dictionary,result_2))
on ready {
foreach (item_x, split ("grassland_discovery;plains_discovery;desert_discovery;tundra_discovery;swampland_discovery;forest_discovery;mountains_discovery;hills_discovery;wasteland_discovery;coastland_discovery",";")) {
if (result_2 = item_x) {
list remove (data_object.homeland_events_string_list, result_2)
}
}
}
} else {
msg ("There seemingly is nothing left to explore in this area.")
}
}
}
]]></function>
<function name="travel_function">
show menu ("Where do you wish to travel?",data_object.travel_string_list,false) {
if (not game.pov.parent = GetObject (result)) {
game.pov.parent = GetObject (result)
} else {
msg ("You are already at this area.")
ask ("Try again?") {
if (result=true) {
travel_function
} else {
msg ("You realize that you need to discover a new area to travel to first, before you can travel to that place.")
}
}
}
}
</function>
</asl>


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

the Script Dictionary contains the actual "script events", so we don't want to add~remove these, and thus instead, we use the "StringList" to add~remove our "events' labels~names", which determine (by matching up to the ScriptDictionary, and thus in effect, "calling upon" that Script from the ScriptDictionary) what events can be run and~or when they can or will be run.

StringList:

1. HK
2. Michael

result_x = StringListItem (Object.StringList, GetRandomInt (0, ListCount (Object.StringList) - 1))

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

// result_x is what script we're "calling upon" in the Script Dictionary below
// if result_x = HK, then run_laps
// else if result_x = Michael, then do push ups

ScriptDictionary:

1. HK=run_laps
2. Michael=do_push_ups

invoke (Object.ScriptDictionary, result_x)

and after it is run, we remove that event option from the list, so it can't be run again by the Script Dictionary:

list remove (Object.StringList, result_x)

// do note that we're removing the item from the *STRING LIST*, we don't change the Script Dictionary at all, as it is simply a "data base" of:

String_1 = Script_1
String_2 = Script_2
String_3 = Script_3
String_4 = Script_4
etc etc etc

the "invoking" of this "data base" (the Script Dictionary) is based upon the chosen STRING LIST's item (string), but NOT directly upon the Script Dictionary's key item (string).

conceptual:

if (StringList's String = ScriptDictionary's String), then do ScriptDictionary's corresponding Script:

String List:
(1) String
(2) String
(3) String

Script Dictionary:
String_1 = Script_1
String_2 = Script_2
String_3 = Script_3

String List == Script Dictionary:
String__.. == String_1 = Script_1
String__.. == String_2 = Script_2
String__.. == String_3 = Script_3

if (String_List's_String = Script_Dictionary's_String_1), and if (Script_Dictionary's_String_1 = Script_Dictionary's_Script_1), then do~invoke Script_Dictionary's_Script_1

else
if (String_List's_String = Script_Dictionary's_String_2), and if (Script_Dictionary's_String_2 = Script_Dictionary's_Script_2), then do~invoke Script_Dictionary's_Script_2)

else
if (String_List's_String = Script_Dictionary's_String_3), and if (Script_Dictionary's_String_3 = Script_Dictionary's_Script_3), then do~invoke Script_Dictionary's_Script_3

so by adding~removing from the StringList, we determine what script can or will be (or not) run from the ScriptDictionary.

HegemonKhan
here's another example, though it's missing the "removal of topics" code line functionality:

<!--Saved by Quest 5.4.4873.16527-->
<asl version="540">
<include ref="English.aslx" />
<include ref="Core.aslx" />
<game name="Testing Game Stuff">
<gameid>d67ec73f-f879-4911-9d88-c02ea527c534</gameid>
<version>1.0</version>
<firstpublished>2013</firstpublished>
</game>
<object name="room">
<inherit name="editor_room" />
<object name="player">
<inherit name="editor_object" />
<inherit name="editor_player" />
</object>
<object name="wise_owl_npc">
<inherit name="editor_object" />
<alias>Wise Owl</alias>
<displayverbs type="stringlist">
<value>Inquire</value>
</displayverbs>
<inquire type="script">
show menu ("What do you wish to inquire about?", global_data_object.inquire_topics_string_list, false) {
invoke (ScriptDictionaryItem (global_data_object.inquire_topics_script_dictionary, result))
}
</inquire>
</object>
</object>
<object name="global_data_object">
<inherit name="editor_object" />
<inquire_topics_string_list type="stringlist">
<value>princess</value>
<value>sword</value>
<value>dragon</value>
</inquire_topics_string_list>
<dragon_topics_string_list type="stringlist">
<value>weakness</value>
<value>location</value>
</dragon_topics_string_list>
<inquire_topics_script_dictionary type="scriptdictionary">
<item key="princess">
msg ("The princess has been captured by the dragon. Rescue her, and the King will wed thee to her, as his heir to his throne and kingdom! You'll become the new king of this land!")
</item>
<item key="sword">
msg ("Ah the legendary dragon slayer sword, magically forged... and guarded by a powerful sorcerer...")
</item>
<item key="dragon">
show menu ("What about the dragon do you wish to know?", global_data_object.dragon_topics_string_list, false) {
invoke (ScriptDictionaryItem (global_data_object.inquire_topics_script_dictionary, result))
}
</item>
<item key="weakness">
msg ("Only the legendary dragon slayer sword can pierce its adamantium scales and thus kill it")
</item>
<item key="location">
msg ("within the volcano in the lava scorched wasteland far to the eastern edge of this world")
</item>
</inquire_topics_script_dictionary>
</object>
<verb name="inquire">
<property>inquire</property>
<pattern>Inquire</pattern>
</verb>
</asl>

michaelsahn
Can you show how to invoke the ScriptDictionaryItem on command, instead of selecting from a string?

HegemonKhan
<command name="hint_command">
<pattern>hint #text#</pattern>
<script>
foreach (item_key_x, global_data_object.hint_script_dictionary) {
if (text = item_key_x) {
invoke (ScriptDictionaryItem (global_data_object.hint_script_dictionary, text))
} else {
msg ("try another word for your hint topic")
}
}
</script>
</command>

<object name="global_data_object">
<inherit name="editor_object" />
<attr name="hint_script_dictionary" type="scriptdictionary">
<item key="dragon">
msg ("dragon")
</item>
<item key="sword">
msg ("sword")
</item>
<item key="princess">
msg ("princess")
</item>
</attr>
</object>

jaynabonne
There is another approach: keep it a string list, but then hang the scripts off the object and have the strings reference the script attribute names. (I have to run off to work, so I'm not testing this, but maybe you'll get the feel for it):

<object name="Character">
<run type="script">
// do something here
</run>
<jump type="script">
// do something here
</jump>
<hide type="script">
// do something here
</hide>
<actions type="stringlist">
<value>run</value>
<value>jump</value>
<value>hide</value>
</actions>
<speak type="script">
count = ListCount(this.actions)
if (count = 0) {
msg("He ignores you")
} else {
s = StringListItem(this.actions, GetRandomInt(0, count-1))
list remove(this.actions, s)
do(this, s)
}
</speak>
</object>

HegemonKhan
I wasn't really sure with exactly what you want, as the way you phrased your post, and based upon what can be done with a script dictionary, I'm not sure at exactly what you wanted?

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

if you wanted, how to use a ' Command ' for invoking a script from a script dictionary, then my previous post shows that (in the simpliest way posssible).

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

if you just wanted to purely invoke a Script Dictionary's Script, it's:

As a Verb, a Function, a Command, or a Script...

invoke (ScriptDictionaryItem (Object.ScriptDictionary, item_key_string_value)

for example (from my previous post):

invoke (ScriptDictionaryItem (Object.ScriptDictionary, dragon)
outputs: msg ("dragon") -> dragon

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

A Dictionary is not ordered like a List is, so you must select a String of the Script Dictionary's Item Key String Value, as you can't use GetRandomInt...

unless you do something creative like this:

<object name="player">
<inherit name="editor_object " />
<inherit name="editor_player" />
<attr name="changedparent" type="script">
if (not wise_owl_npc.parent = this.parent)
wise_owl_npc.parent = game.pov.parent
}
</attr>
</object>

<object name="wise_owl_npc">
<inherit name="editor_object" />
<attr name="hint" type="script">
invoke (ScriptDictionaryItem (Object.ScriptDictionary, ToString (GetRandomInt (1,3)))
</attr>
<attr name="displayverbs" type="listextend">Hint</attr>
</object>

<object name="global_data_object">
<inherit name="editor_object" />
<attr name="hint_script_dictionary" type="scriptdictionary">
<item key="1">
msg ("dragon")
</item>
<item key="2">
msg ("sword")
</item>
<item key="3">
msg ("princess")
</item>
</attr>
</object>

michaelsahn
Hi Jay -

Thanks for reply. Using your suggestion, I created an object called "poems" with a stringlist "story" and individual strings that link to scripts. Unlike your example, I've made the object global instead of associating it with individual objects.

I created a room ("sitting room") with a script the triggers before the player enters that reduces count of stringlist, and randomly selects a string to trigger a script. This mirrors your suggestion.

So, I can get the count to reduce, and print a message when the count is reduced to 0, but I'm getting an error when it tries to compile the code that randomly selects and runs a script.

I've attached the file. If you could point out my error, I'd very much appreciate it. I'll continue to try to solve this on my own, but I suspect I may be committing some sort of procedural error of which I'm not aware.

Thanks again - I'm getting closer to building the game I'd like.

-Mike

jaynabonne
I believe if you change this:

          do (poems.story, s)

to this:

         do (poems, s)

it will work.

The scripts live on the poems object, not the story string list.

michaelsahn
Success! I tested the code and it all does what I need it to do.

I now have the basic components to build. I'm sure I'll have more questions in the future, but this is a huge leap forward for me.

Thank you!

Mike

HegemonKhan
ah, you just wanted to know how you run a script on an object... I was leaning towards this as a guess to what you were asking for, but Jay already got it covered.

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

just my own commentary, as well:

Scripts on Objects, are, in fact, Attributes, just like any other type of Attribute.

Attribute Types: Strings, Integers, Doubles~Floats~Floating_Points, Booleans~Flags, Lists, Dictionaries, and Scripts.

Object.Attribute = Value_or_Expression

HK.favorite_color = black // Type: String
HK.strength = 100 // Type: Integer
HK.damage = 123.825 // Type: Double
HK.dead = false // Type: Boolean
HK.clothes ( = pretend String_or_Object List ) // Type: List
HK.fight ( = pretend script block ) // Type: Script

so... as Jay already shown:

invoke (HK.fight)
do (HK, "fight")

<object name="HK">
-> <inherit name="editor_object" />
-> <attr name="fight" type="script">
->-> // whatever scripts
-> </attr>
</object>

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

whereas, a Script Dictionary, is really more when you want to 'select' amongst many scripts. Basically, a script dictionary is just a bunch of "ifs", just as the "switch" is just a bunch of "ifs" too.

Multiple Ifs:

if (topics)
-> if (dragon)
->-> msg (.....)
-> else if (sword)
->-> msg (....)

is the same as a Switch:

switch (topics)
-> case (dragon)
->-> msg (.....)
-> case (sword)
->-> msg (......)

is the same as a Script Dictionary:

result = dragon_or_sword
invoke (ScriptDictionaryItem (Object.topics, result))

-----------

A Script Dictionary is the most efficient method to use of the above methods, as it uses~needs the least amount of code lines (especially the more complex your game is with lots of scripts to select from).

jaynabonne
An important note regarding these two commands:

invoke (HK.fight)
do (HK, "fight")

There is a crucial difference between them, which is that if you use "do", then you will have a "this" parameter for the script to use (HK in this case), whereas in the "invoke" case, you will not. The latter just takes the value of the script and invokes it. You can simulate this by creating a parameters dictionary with the "this" parameter yourself ("do" and "invoke" have variants with another parameter, which is a dictionary of parameters to pass to the script), but it's more work. That is a downside to script dictionaries - you can only use invoke. I personally made a small contribution to the Quest source by simulating the "this" parameter for internal invokes.

And for me, a key difference between if/switch and a script dictionary is that the script dictionary case can be dynamically modified at run time (adding and removing scripts) whereas the if/switch code is hard-wired. Of course, if you fixed choices, then that doesn't matter.

Finally, on a more theoretical front, you can view a Quest object as being a dictionary of sorts - you look up values by name... except an object can hold values of all sorts, and you can more easily update them. Quest dictionaries can be a real pain to modify at run-time, as you have to actually remove a value before setting a new one for the same key. Objects don't have that problem - but then creating and manipulating objects at run-time is also a pain. :) No easy answer for all cases, but on a case-by-case basis, one approach usually works better than the other.

It's funny, but I just realized that I've never used a script dictionary in my code... :)

michaelsahn
Part of the pleasure of leaning to code is that there are many ways to achieve the goal. It's a lot like writing itself - I first try to get what I want by writing whatever I happen to know, then I iterate on making everything clearer and more efficient. It's delightful when everything works.

Thanks again!

MIke

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

Support

Forums