Following several years of playing
Zombies, Run!, an urge to duplicate the in-game voiceover hit me. You know the voice that informs you of what you've collected every now and then? Yeah, that one.
Perhaps it was acute boredom or professional curiosity, but that itch would not go away... and today's web tutorial is the result. I really got a kick out of this piece of work, and hope you do too. It's basically a lot of string concatenation in conjunction with the VBScript code I
wrote a while back.
So, the objective is to have a program that will say random things like...
"Collected a toolbox."
"Collected a toolbox and an overcoat."
"Collected a toolbox, a box of lightbulbs and two spades."
"Collected a toolbox, a sports bra, three books, five footballs and four handfuls of money."
Hold on, what does this actually do?
Nothing useful, I'm afraid. It's an utterly trivial task. I'm like a kitten with a ball of wool at this point.
Without further ado...
Let's begin with the speech code and a randomizer function. Simple enough, right? Upon running it, you get a voice that says "Collected a", and that's it.
Dim speech
Function getrandomno(min, max)
Randomize
getrandomno = (Int((max - min + 1) * Rnd + min))
End Function
speech = "Collected a "
Set VObj = CreateObject("sapi.spvoice")
VObj.Speak speech
Now, you'll want to actually have the voice tell you what items you picked up. Declare two more variables,
item and
item_name. Then declare an array,
items, with four elements. Each of these will be an object you picked up.
Dim speech
Dim item, item_name
Dim items(4)
items(0) = "spade"
items(1) = "toolbox"
items(2) = "sports bra"
items(3) = "shotgun"
Function getrandomno(min, max)
Randomize
getrandomno = (Int((max - min + 1) * Rnd + min))
End Function
speech = "Collected a"
Set VObj = CreateObject("sapi.spvoice")
VObj.Speak speech
Finally, set
item to a random number between 0 and 3. Then set
item_name to the string inside the array
items, defined by
item. Remember to add a space before it! After that, concatenate
item_name to
speech. Now the voice should say stuff like "Collected a toolbox" or "Collected a spade", randomly!
Dim speech
Dim item, item_name
Dim items(4)
items(0) = "spade"
items(1) = "toolbox"
items(2) = "sports bra"
items(3) = "shotgun"
Function getrandomno(min, max)
Randomize
getrandomno = (Int((max - min + 1) * Rnd + min))
End Function
item = getrandomno(0, 3)
item_name = " " & items(item)
speech = "Collected a"
speech = speech & item_name
Set VObj = CreateObject("sapi.spvoice")
VObj.Speak speech
Yep, give yourself a pat on the back. You've just created the core of what is to be our totally spectacular (but still utterly useless, heh heh) program. Now, not everything is prefaced with "a". You can have "a spade" or "a shotgun", but not "a overcoat". It's simply not something you can take for granted, the English language being what it is. With that in mind, some changes are required.
Change
items to a two-dimensional array. The first element is the item name, and the second is the "a" or "an".
Dim items(4)
items(0) = Array("spade", "a")
items(1) = Array("toolbox", "a")
items(2) = Array("sports bra", "a")
items(3) = Array("shotgun", "a")
Let's add a new item to the array
items.
Dim items(5)
items(0) = Array("spade", "a")
items(1) = Array("toolbox", "a")
items(2) = Array("sports bra", "a")
items(3) = Array("shotgun", "a")
items(4) = Array("overcoat", "an")
Declare a new variable,
quantity, that will hold either "a" or "an".
Dim speech
Dim item, item_name, quantity
Also ensure that you now get a random number from 0 to 4 instead. And set
quantity to the first element of the array element in
items referenced by
item!
item = getrandomno(0, 4)
quantity = items(item)(1)
Change the following lines. Now instead of just referencing one array element from
items, we derive
item_name from the first array element of that array element, and
quantity! And we remove the hardcoded "a" from
speech. So now, sometimes we'll get "Collected an overcoat"!
item_name = " " & quantity & " " & items(item)(0)
'speech = "Collected a"
speech = "Collected"
Let's take this further. What if sometimes we want a
collective item instead of singular? Like, "a box of lightbulbs" or "a handful of money"?
Simple. We extend the
items array, by making each item an array of four elements instead of two. The third element is the collective noun, and the fourth is the "a" or "an". In the latest two elements we've added, the second element is always an empty string. With singular items, the third and fourth elements are always empty strings.
Dim items(7)
items(0) = Array("spade", "a", "", "")
items(1) = Array("toolbox", "a", "", "")
items(2) = Array("sports bra", "a", "", "")
items(3) = Array("shotgun", "a", "", "")
items(4) = Array("overcoat", "an", "", "")
items(5) = Array("lightbulbs", "", "box", "a")
items(6) = Array("money", "", "handful", "a")
Add a new variable,
unit.
Dim speech
Dim item, item_name, quantity, unit
Make sure the random number is now between 0 to 6.
unit is initially an empty string.
item = getrandomno(0, 6)
quantity = items(item)(1)
unit = ""
Now, if the third element of the array element is
not an empty string, that means it requires a unit. In this case, set
unit to the value of that third element and concatenate " of" to it. Set
quantity to the fourth element instead of the second.
item = getrandomno(0, 6)
quantity = items(item)(1)
unit = ""
if items(item)(2) <> "" then
unit = " " & items(item)(2) & " of"
quantity = items(item)(3)
end if
Then concatenate
unit between
quantity and the
item name (ensuring there are spaces between them as well), to get
item_name! Now you should occasionally get "Collected a box of lightbulbs" or "Collected a handful of money".
item = getrandomno(0, 6)
quantity = items(item)(1)
unit = ""
if items(item)(2) <> "" then
unit = " " & items(item)(2) & " of"
quantity = items(item)(3)
end if
item_name = " " & quantity & " " & unit & " " & items(item)(0)
Now for something trickier...
What if you wanted
plural items? Like "spades" instead of "spade"? Not every plural simply has an "s" appended to the end. "Boxes" and "Hippopotami" are strong examples.
For this, we'll need to modify the
items array again. This time, each element should be an array of five elements instead of four. The fifth element (here's looking at you, Bruce Willis!) will hold the plural form of the
item name, or the plural form of the collection.
items(0) = Array("spade", "a", "", "", "spades")
items(1) = Array("toolbox", "a", "", "", "toolboxes")
items(2) = Array("sports bra", "a", "", "", "sports bras")
items(3) = Array("shotgun", "a", "", "", "shotguns")
items(4) = Array("overcoat", "an", "", "", "overcoats")
items(5) = Array("lightbulbs", "", "box", "a", "boxes")
items(6) = Array("money", "", "handful", "a", "handfuls")
Then we set quantity to a random number between, say, 1 to 5.
item = getrandomno(0, 6)
quantity = getrandomno(1, 5)
At the
If block, we add an
Else condition, and surround the entire block with another
If block.
if quantity = 1 then
if items(item)(2) <> "" then
unit = " " & items(item)(2) & " of"
quantity = items(item)(3)
else
quantity = items(item)(1)
end if
else
end if
quantity is singular, so we'll replace the value of
quantity with the appropriate "a" or "an".
item_name is, quite naturally, the first element of the array.
if quantity = 1 then
if items(item)(2) <> "" then
unit = " " & items(item)(2) & " of"
quantity = items(item)(3)
else
quantity = items(item)(1)
end if
item_name = items(item)(0)
else
end if
If
quantity is more than 1, we use the plural form (the fifth element). And instead of "a" or "an", we use the number! So, no changes to
quantity.
If the item is collective, we use the plural form of the collective noun. If not, we simply use the plural form of the item.
Now, you're going to get stuff like "Collected 5 shotguns" or "Collected 3 boxes of lightbulbs", in addition to the ones you already have!
unit = ""
if quantity = 1 then
if items(item)(2) <> "" then
unit = " " & items(item)(2) & " of"
quantity = items(item)(3)
else
quantity = items(item)(1)
end if
item_name = items(item)(0)
else
if items(item)(2) <> "" then
unit = " " & items(item)(4) & " of"
item_name = items(item)(0)
else
item_name = items(item)(4)
end if
end if
For the final trick...
What if you wanted different items in different quantities? Like, "a spade and three boxes of lightbulbs"? This one gets a bit more complicated, so I'll need you to pay attention.
First, we need a new variable,
no_items.
Dim speech
Dim item, item_name, quantity, unit, no_items
Then we need two new arrays,
collections and
used. We'll arbitrarily assume that the largest number of different item types will be 5.
Dim speech
Dim item, item_name, quantity, unit, no_items
Dim collections(5), used(5)
After the
getrandomno() function, we're going to set
no_items to a number between 0 and 4.
Function getrandomno(min, max)
Randomize
getrandomno = (Int((max - min + 1) * Rnd + min))
End Function
no_items = getrandomno(0, 4)
Now, wrap all that code we wrote earlier, in a
For loop iterating from 0 to
no_items. The steps will be there - we're just going to repeat it
i number of times.
no_items = getrandomno(1, 4)
for i=0 to no_items step 1
item = getrandomno(0, 6)
quantity = getrandomno(1, 5)
unit = ""
if quantity = 1 then
if items(item)(2) <> "" then
unit = " " & items(item)(2) & " of"
quantity = items(item)(3)
else
quantity = items(item)(1)
end if
item_name = items(item)(0)
else
if items(item)(2) <> "" then
unit = " " & items(item)(4) & " of"
item_name = items(item)(0)
else
item_name = items(item)(4)
end if
end if
item_name = " " & quantity & " " & unit & " " & item_name
next
At the end of it, set the current element of the
collections array to the value of
item_name.
no_items = getrandomno(1, 4)
for i = 0 to no_items step 1
item = getrandomno(0, 6)
quantity = getrandomno(1, 5)
unit = ""
if quantity = 1 then
if items(item)(2) <> "" then
unit = " " & items(item)(2) & " of"
quantity = items(item)(3)
else
quantity = items(item)(1)
end if
item_name = items(item)(0)
else
if items(item)(2) <> "" then
unit = " " & items(item)(4) & " of"
item_name = items(item)(0)
else
item_name = items(item)(4)
end if
end if
item_name = " " & quantity & " " & unit & " " & item_name
collections(i) = item_name
next
After setting speech to "Collected", instead of concatenating one
item_name to
speech, we add all the strings from the
collections array. Use a
For loop to iterate from 0 to
no_items.
speech = "Collected"
for i = 0 to no_items step 1
next
'speech = speech & item_name
If we're not at the end of the list of items, concatenate the value of the current element of
collections and a comma to
speech.
for i = 0 to no_items step 1
if i < no_items then
speech = speech & collections(i) & ","
else
end if
next
If we're at the end of the list, there are two possibilities. One, there is only one item type in the
collections array, i,e,
i is 0. The other is that there are multiple item types and we're at the end.
for i = 0 to no_items step 1
if i < no_items then
speech = speech & collections(i) & ","
else
if i = 0 then
else
end if
end if
next
If it's the former, just concatenate the value of the current element of
collections to
speech. If there were more than one item and we're at the end, concatenate to
speech the string " and" (take note of the space!) followed by the value of the current element of
collections.
for i = 0 to no_items step 1
if i < no_items then
speech = speech & collections(i) & ","
else
if i = 0 then
speech = speech & collections(i)
else
speech = speech & " and" & collections(i)
end if
end if
next
Now try it!
For the most part, you'll get beautifully constructed speeches like "Collected a sports bra, three spades, two boxes of lightbulbs and a handful of money". But wait... there's a problem. Once in a while, the randomizer repeats itself. So sometimes you may get something like "Collected a spade and two spades". That's not what we want at all, so...
Here, the
used array will store all the unique numbers that have been generated.
Remember the line to randomize what item you get? Well, wrap that in a
Do-while loop. It should execute at least once. And keep executing while the returned value of the
inuse() function with
item passed in as an argument, is
true. So if the loop is exited,
item is guaranteed to be unique within the
used array.
And then set the current element of the
used array to
item.
for i = 0 to no_items step 1
do while inuse(item) = true
item = getrandomno(0, 6)
loop
used(i) = item
quantity = getrandomno(1, 5)
Then let's write the
inuse() function. This basically tells you if item is already taken.
Function getrandomno(min, max)
Randomize
getrandomno = (Int((max - min + 1) * Rnd + min))
End Function
Function inuse(num)
End Function
The parameter is
num. Declare
dupe as a variable and set it to
false. At the end of the function, return
dupe.
Function inuse(num)
Dim dupe
dupe = false
inuse = dupe
End Function
Run a
For loop that iterates through
no_items. If at any point the value of the current element of
used matches
num,
dupe is set to
true.
Function inuse(num)
Dim dupe
dupe = false
for j = 0 to no_items step 1
if used(j) = num then
dupe = true
end if
next
inuse = dupe
End Function
There, you've got it!
Just for shits and giggles, let's add a whole lot more stuff into the
items array!
Dim items(15)
items(0) = Array("spade", "a", "", "", "spades")
items(1) = Array("toolbox", "a", "", "", "toolboxes")
items(2) = Array("sports bra", "a", "", "", "sports bras")
items(3) = Array("shotgun", "a", "", "", "shotguns")
items(4) = Array("overcoat", "an", "", "", "overcoats")
items(5) = Array("lightbulbs", "", "box", "a", "boxes")
items(6) = Array("money", "", "handful", "a", "handfuls")
items(7) = Array("axe", "an", "", "", "axes")
items(8) = Array("trousers", "", "pair", "a", "pairs")
items(9) = Array("food", "", "tin", "a", "tins")
items(10) = Array("book", "a", "", "", "books")
items(11) = Array("fuel", "", "can", "a", "cans")
items(12) = Array("pain meds", "", "bottle", "a", "bottles")
items(13) = Array("football", "a", "", "", "footballs")
items(14) = Array("radio", "a", "", "", "radios")
Don't forget to change the random number generator code!
item = getrandomno(0, 14)
Have fun with your program! Better yet, if you haven't already, check out
Zombies, Run!.
Collected a thank-you, good-bye and a see-you-again!
T___T