It looks like you're new here. If you want to get involved, click one of these buttons!
window.Weapon = function(name, cost, damage) { this.name = name; this.cost = cost; this.damage = damage; } window.Weapon.prototype.hit = function() { return random(0,this.damage); }; window.Weapon.prototype.prettyName = function() { return name; };
<<set $dagger = new Weapon("dagger",30,3); $smallsword = new Weapon("small sword",50,3)>> <<set $weapon = $dagger>>
Your current weapon is a <<$weapon.prettyName()>>. <<if $weapon == $dagger>>\ [[buy small sword]] (30 coins, does 3 damage)\ <<elseif $weapon == $smallsword>>\ [[buy big sword]] (50 coins, does 5 damage)\ <<endif>>
$weapon.prettyName()(without the double braces).
$weapon.namecorrectly displaying "dagger".
Your current weapon is a $weapon.It will display [object Object] as expected. But I would like to define a prototype function, such as
toString()that returns the correct string version of the object to print.
Comments
First. By "an init passage", may I assume you meant the StoryInit special passage?
Second. Because of the way variables in most story formats are cloned (or something similar) between passages (required for the history to work), assigning $dagger to $weapon isn't going maintain the reference beyond the initial passage where the assignment was made (because of the cloning). Basically, do not depend on reference equality from one passage to the next.
You'll probably want to do something like the following instead: To access the specific Weapon object, you'd do something like: To check if $weapon is a specific Weapon could be done in a couple of different ways:
If you're going to use SugarCube 2, I'd suggest reading its documentation (specifically on: naked $variables, the <<print>> macro and its aliases <<=>> & <<->>). To print an expression, which includes method calls like $weapon.prettyName(), you should probably use one of the following:
Beyond that, and bearing in mind the reference issue suggestion I made above, I'd suggest the following:
I've already noted, above, what you were doing wrong with how you were trying to print prettyName(). A custom toString() method for Weapon, while easy enough to create, wouldn't really help you out due to the way printing currently works. So, I'd recommend simply calling prettyName() for now.
Answered above.
I did attempt but it gave a garbage answer. The culprit was that I was returning name instead of this.name.
Are the environment data all cloned between passages? If you have a lot of lookup tables, would that be a performance concern, particularly since those are constant (unnecessary copy)?
You mentioned that having the weapons based on a prototype is old school, do you recommend any better way of doing it?
I've also read this discussion on keeping JS objects in variables and it seems that saving game state where the state is comprised by many complex objects (ex. if the player is a PC object, and every NPC is an NPC object, all with properties that are references to other objects) would be very cumbersome. Would you recommend having as flat a system as possible using Maps instead of objects as much as possible?
This was a test RPG story so feel free to give as much "overhaul" feedback as possible, as this was just testing out different ways of setting up a RPG framework.
If you're creating objects which are essentially static (i.e. unchanging), then you would probably be better served by not placing them within story variables. I'd probably suggest creating properties on either the setup object (provided by SugarCube) or the global window object. As long as you don't trample existing properties, window is a popular choice since its properties become auto-globals.
That's not what I meant. Also, all objects in JavaScript are prototypal regardless of the syntactical sugar involved (i.e. whether you're using the original prototypal syntax, the classical syntax, or the newer class syntax).
I meant that the way you're doing it is old school. By simply assigning methods onto the prototype property, you're not setting the property descriptors (e.g. whether they're enumerable). It's not a big deal. As I noted, it works.
It would be simpler, cognitively speaking, to use property bags (whether done via generic or Map objects), rather than more complex objects. The difference between the two, for the purpose of this discussion, being that the former are essentially collections of key/value pairs, while the latter may be instantiated and, generally, include behaviors.
The essential idea from the thread you linked is that to properly re-instantiate any complex object, the deserialization code needs to know how to do so. The most common basic types (value types, generic objects, arrays, Date, RegExp, Map, Set) are covered. Custom complex object types need to define a toJSON method which does the requisite setup during serialization. While not really complex to do, it does require some knowledge and effort.
So, as you're a beginner, I suppose that I'd say yes. If you can get away with it, then I would recommend sticking with property bags (i.e. data only objects), whether you're using generic or Map objects. Quite often, that's all you really need anyway.
(inside Story JavaScript or in the StoryInit passage (wouldn't that make them story variables))
You mentioned that window's properties become auto globals, so if I define Inside the Story JavaScript,
I should be able to in a passage ask for It throws the error of not being able to read property 'name' of undefined; meaning it can't find the PC global object...
I'm a beginner in Twine and SugarCube, but somewhat experienced in programming: so feel free to explain in as much technical depth as you need :P.
Thanks again for the help!
The StoryInit passage is for any macro based code that you want run before the first passage is shown, you can use it to setup default values for your story variables like so.
The reason your <<= $PC.name>> code did not work is because you only use a $ sign when referencing a SugarCube story variable. The window.PC property/variable you created in your example is a Javascript property/variable so you would reference is like so.
A story variable is, usually, one created/accessed within macros and is prepended by the $ (dollar sign) sigil. For example: Note: The $ sigil (i.e. when prepended to the front of an identifier) is only special, denoting a story variable, within macros (and the naked $variable formatter). Within pure JavaScript code it has no special meaning (though SugarCube does include jQuery, so the $ by itself is an alias for it).
You may also create/access story variables within purely JavaScript code by accessing the State.variables object (which is where the story variables actually live). For example, the following JavaScript is equivalent to the TwineScript above:
A property bag is a generic term for a simple object which contains key/value data pairs (i.e. a container of data properties). You've already seen and used examples of them. For example, using a generic object which only consists of data properties:
If the Map object is more your style, you can do essentially the same thing with it, though accessing the values will be different:
I recommend that all story initialization be done either within a JavaScript section (Story JavaScript in Twine 2, a script tagged passage in Twine 1) or the StoryInit special passage. Both are executed exactly once at story startup/initialization (to be clear, unlike most of the Story… and Passage… special passages, the StoryInit special passage is not reprocessed upon passage navigation).
As to which one to use for what, apples to apples and all that. I generally recommend putting any purely JavaScript setup code within your story's Story JavaScript section and any macro/TwineScript setup code within your story's StoryInit special passage.