0 votes
by (8.9k points)

Could anybody give me a broad idea of how to do this?  I'm not sure if I need to be using loops, or arrays, or Javascript, or what...

In this passage, the player has visited a sword shop.  I would like it to randomly generate a list of available swords.  Each sword should have the following variables:

$description $material $class $price

$description might be "beautiful" or "brutal looking".  $material might be "steel" or "iron".  $class might be "longsword" or "short sword".

So the player would see something like:

- - - - - -

The smithy has the following swords on display:

  • A beautiful steel longsword.  It costs 25 gold.  [[Buy it]]
  • A brutal looking iron short sword.  It costs 15 gold.  [[Buy it]]

- - - - - -

When the player clicks the [[Buy it]] link, his $gold is reduced by $price and the item is added to his inventory.

Can anybody could give me some guidance on how to go about doing this?  I'm not really sure where to start!

1 Answer

+2 votes
by (63.1k points)
selected by
 
Best answer

Here's some example code for a fairly simple sword generating widget:

::StoryInit
<<set setup.swordTypes to [
	'short sword',
	'long sword',
	'great sword'
]>>

<<set setup.swordMats to [
	'iron ',
	'steel ',
	'adamantium '
]>>

<<set setup.swordDescr to [
	'an ordinary ',
	'a simple ',
	'a mean-looking ',
	'a beautiful ',
	'a well-crafted '
]>>

::swordGenerator
<<widget 'newSword'>>\
	<<silently>>
		<<set _quality to 0>>
		<<set _price to 25>>
		
		/% select qualities randomly %/
		<<set _type to random(setup.swordTypes.length - 1)>>
		<<set _material to random(setup.swordMats.length - 1)>>
		<<set _descr to random(setup.swordDescr.length - 1)>>
		<<set _str to setup.swordDescr[_descr] + 
			setup.swordMats[_material] +
			setup.swordTypes[_type]>>
		
		/% set sword's price %/
		<<set _quality to _type + _material + _descr>>
		<<set _price to _price + (_quality * 5)>>
		
		/% build sword %/
		<<set _sword to {
			qual  : _quality,
			price : _price,
			type  : _type,
			matl  : _material,
			descr : _descr,
			str   : _str.toUpperFirst()
		}>>
	<</silently>>\
<</widget>>

::make some swords
<<silently>>
<<set $swords to []>>
<<set _numberToMake to 4>> /% set how many swords you want to generate %/
<<for _i to 0; _i lt _numberToMake; _i++>>
	<<newSword>>
	<<set $swords[_i] to _sword>>
<</for>>
<</silently>>\
\
<<for _i to 0; _i lt $swords.length; _i++>>\
	<<capture _i>>\
		<<print $swords[_i].str>>. It costs <<print $swords[_i].price>> gold.
	<</capture>>\
<</for>>

This widget allows us to create a bunch of swords as objects by pulling from the setup arrays we have in our StoryInit.  Higher quality options should be later in the arrays.  This saves us time on trying to associate each option with a certain level of quality.  To add new options, just change the arrays in StoryInit; no other modifications will be necessary.

I can't help you with your 'buy' links until I know how you plan to store the weapons the player buys in their inventory, how your inventory is set up, and how that whole aspect of the game is supposed to work.  If you provide more information/code, I'll try to help there as much as I can.

by (8.9k points)

That definitely works!  Thank you so much!  But I don't know why.  :-/

Would it be okay if I asked you a series of really annoying questions that would help me understand why this code works as it does?  (This stuff doesn't appear to be in the documentation.)

My first annoying question is, why does:

random(setup.swordTypes.length - 1)

...need the ".length - 1" bit?  To an untrained eye, random(setup.swordTypes) ought to return a random .swordType.

by (63.1k points)
edited by

Would it be okay if I asked you a series of really annoying questions that would help me understand why this code works as it does?

Absolutely.

My first annoying question is, why does:

random(setup.swordTypes.length - 1)

...need the ".length - 1" bit?  To an untrained eye, random(setup.swordTypes) ought to return a random .swordType.

 This let's break this question down into two parts: first, let's talk about arrays, then we'll talk about the random() function.

Arrays

Arrays in SugarCube are based on JavaScript arrays.  Therefore, the following things are true:

  1. Arrays hold an ordered collection of data.  Each value in an array is mapped to a number.  These numbers are called indexes (or indices) ("index" is the singular form).
  2. Arrays are zero based, meaning the first element in an array is at index 0.
  3. The length property of an array returns the number of items in an array; it also counts the 0th index.

Examples:

<<set $array to ['first', 'second', 'third']>>
<<print $array.length>> /% prints 3 %/
<<print $array[0]>> /% prints 'first' %/
<<print $array[$array.length - 1]>> /% prints 'third' %/
/% $array.length = 3, $array.lingth - 1 = 2, since 0 if first item, 2 is third/last item %/

<<set $otherArray to ['hello']>>
<<print $otherArray.length>> /% 1 %/
<<print $otherArray[0]>> /% 'hello' %/
<<print $otherArray[$otherArray.length - 1]>> /% 'hello' %/
<<print $otherArray[$otherArray.length]>> /% nothing, since this index is empty %/

 The random() function.

In SugarCube, the random function accepts up to two numbers, and returns a random whole number between those values.

<<set _diceRoll to random(1, 6)>> /% returns a number between 1 and 6 %/

 If the first number is omitted, it sets the first value to 0.

<<set _diceRoll to random(6)>> /% returns a random number between 0 and 6 %/

Explanation

So, the reason I'm using random on the length - 1 is because I want to get the number of an index from the array.  I then use this number to access the array and form the description, and further use this number to set the quality/price of the generated item: a higher index means it pulls a higher quality option from the array, so the price should be higher.

There is an Array.random() method that we could use instead to return random values from the array, but I wanted the number as well.  The random() function we used only accepts numbers.

Does that make sense?

by (8.9k points)

Yes, that makes perfect sense!  Thank you!  Before you explained that, I didn't realise that _type, _material and _descr returned numbers, not values from the arrays.

Could you explain _str for me?  I think in this first instance:

		<<set _str to setup.swordDescr[_descr] + 
			setup.swordMats[_material] +
			setup.swordTypes[_type]>>

_str picks the .swordDescr value from the array that matches the randomly generated number _descr, and so forth.

I can't exactly grasp how this bit works:

/% build sword %/
		<<set _sword to {
			qual  : _quality,
			price : _price,
			type  : _type,
			matl  : _material,
			descr : _descr,
			str   : _str.toUpperFirst()
		}>>

 

by (63.1k points)

In JavaScript, and SugarCube, [] (square brackets) denote an array, while {} (curly braces) denote an object.  An object is sort of like a package or collection of variables.

<<set $object to {
    property : 'value'
}>>
<<print $object.property>> /% 'value' %/

You've been using objects this whole time: the properties inside objects are essentially variables.  For example:

<<set $variable to 'something'>>
<<print $variable>> /% 'something' %/
<<print State.variables.variable>> /% 'something' %/

The setup object we used earlier is similar, but it's better than using variables, because the State object can slow down the game if it gets too cluttered, since each new passage/turn in your game gets its own copy of the entire State.  This is how SugarCube's save system and history system (i.e. the back/forward buttons) work.  However, it's a bad practice to gum up the State when we don't need it; values that don't change (much) shouldn't be a part of the State.  SugarCube provides the setup object for us to use as an alternative to using the State or global objects.

Additionally, pretty much every variable is a property of some object--if we set a global JavaScript variable, it's actually a property on an object called 'window' (in a web browser, at least).  This is the foundation of things you might have heard about (namespace, scoping, etc).

I digress.  We make an object called _sword and attach a bunch of properties to it so that we can 'return' the entire sword object when we say something like:

<<newSword>>
<<set $mySword to _sword>>

<<print setup.swordTypes[$mySword.type]>> /% prints either 'long sword', 'short sword', or 'great sword' %/
<<print $mySword.price>> /% prints the price of the sword %/
/% etc %/

Passing the generated sword around as an object allows us to access all the variables we've set for it, but also makes it easier to handle; we only need to pass the object, not a series of individual variables.  This has other advantages, too--as you can see from the last bit of code in the original answer, we can create an array of sword objects and print each one without having to assign each one to specific variables, meaning there's functionally no limit on the number of swords we could create (though we could crash the browser if we try to make an absurd amount).

by (8.9k points)
Okay, thanks so much, I see how everything works now!  I just have one further annoying question: what's the difference between $variable and _variable?
by (63.1k points)

The $ denotes a story variable, while _ denotes a temporary variables.  As mentioned earlier, you shouldn't clutter the state with variables you don't need.  Temporary variables only last for the 'moment' they are created in, i.e. they are removed from the State after passage transition.  This means that you have to set the _sword variable to another variable if you want it to persist, but it also means you can prevent the State from being dragged down by hundreds of swords if the player visits the the shop over and over.

More info here.

by (8.9k points)
Thank you, Chapel, I've really learned a lot.
by (110 points)
hey  thank u. this was very helpful. Is it possible to add matching Pictures to each generated item?
...