That said, however, I usually find it cleaner to use alternate quoting characters (single quote vs. double quote), rather than having to escaping the same quotes. For example:
Upgraded to SugarCube 2.x since my last post. Still using twine 1.
Here I'm trying to make random discovery/encounter work but apparently visited() doesn't work with disabled history(I think?) and what I'm trying below doesn't work, as usual.
Main passage:
@
<<for $i to 0; $i lt $sewerVisited.length; $i++>>
[[$sewerVisited[$i]]]<</for>><</if>>
Location:
<<if ndef $visited.passage()>>
<<set $sewerVisited.push(passage());
$sewerExplore.splice($sewerExplore.indexOf(passage()),1);
$visited.passage() to 1>><</if>>
[[Return|sewerHub]]
.. apparently visited() doesn't work with disabled history
The visited function works by looking through History (State), thus if there is no History then it can't determine if a passage has been visited.
If I understand your request correctly you want to track which passages the reader has visited and your story does not allow the reader to undo a choice.
I see you have a $sewerExplore array storing possible target passage names and a $sewerVisited array tracking which passages have been visited. I am confused by your $visited variable, it appears to be an object with a passage method.
One question, do you want to track every passage the reader has visited in your story, or only ones within a section (Sewers) of the story?
There are two major errors in your Location passage:
1. the ndef operator is used to check if something is defined but you are using it to check the return of the $visited.passage() call, if you are trying to check if the $visited.passage method exists then try removing the parenthesis () it may work.
2. You are trying to assign a number 1 to the $visited.passage() method which is invalid, I don't know exactly what you are trying to do with that <<set>> so I can't offer a solution.
I'm trying to find a different way to check if passage has been visited, idea with $visited was that if passage() doesn't match anything in the object it creates new entry. That way I could just paste it into any passage that I need checked.
You did not answer my question
Do you want to track every passage the reader has visited in your story, or only ones within a section (Sewers) of the story?
If you want to track every passage you could do the following:
1. StoryInit special passage:
<<set $visited to []>>
2. PassageReady special passage:
<<set $visited.push(passage())>>
3. Test if you have been here before:
<<set $count to $visited.count(passage())>>
<<if $count > 1>>
You have been to this passage $count times!
<</if>>
4. A list of passages visited including the current one:
You have visited:
<<for $i to 0; $i lt $visited.length; $i++>>
$visited[$i]
<</for>>
Sorry for lack of clarity. I meant for it to work only in passages I paste that code in.
I tried to avoid using .push outside an if because it would add a new line to object every time that passage is visited when it just needs to be set once.
I guess I will have to resort to manually adding every passage to $visited.
If you want a passage name to only appear in the $visited collection only once you can check for the value before trying to add it again. Replace point 2 in my previous comment with:
2. Just add the following to the passages you want to track.
<<if not $visited.contains(passage())>>
<<set $visited.push(passage())>>
<</if>>
To me, it sounds like $visited is a generic object being used something like a set, not an array, so $visited.push(passage()) isn't going to work properly. How are you initializing $visited?
Anyway, going on the assumption that $visited is a generic object, I believe something like the following is what is called for, which is just a minor change really. Your main issue was using the dot notation (e.g. $visited.passage()) when you should have been using the square bracket notation (e.g. $visited[passage()]). For example:
Since you're removing the locations from $sewerExplore as they're discovered, you don't need $visited as you may simply check $sewerExplore for membership.
Repeating your location code is unnecessary, it's boilerplate. If you provide some way for the code to only run on the sewer passages, you could put it into the PassageReady special passage. An easy method would be to use a passage tag (e.g. sewer).
<<for $i to 0, $keys to Object.keys($object).sort(); $i lt $keys.length; $i++>>
<<click $object[$keys[$i]].name>><<set $object[$keys[$i]].amount-->><</click>>
<</for>>
Link generates fine, but when I click on it it just throws this error:
<<set>>: bad evaluation: State.variables.object[State.variables.keys[State.variables.i]] is undefined.
Also tried
.sort(function(a,b){return b.amount-a.amount})
to sort by amount but I guess I'm doing something wrong there as well.
The contents of the <<click>> macros you're generating are evaluated only when the link is clicked by the player, which is long after this generating code has run its course. By that point, several of your $variables are no longer relevant and, in one case, defined. You're going to need to capture the values of these $variables (specifically, $key and $i) early, rather than late. Since we're dealing with macros, that means you'll need to <<print>> them.
The effect of what you need to do is somewhat like using a closure in JavaScript.
to sort by amount but I guess I'm doing something wrong there as well.
You're sorting an array of keys, so your compare function is being passed keys, however, you're treating its parameters as if they were objects. For what you want to do, you need to use the keys on $object.
For example (incl. your sort function):
<<for
$i to 0, $keys to Object.keys($object).sort(function (a, b) { return $object[b].amount - $object[a].amount });
$i lt $keys.length;
$i++
>>
<<print '<<click $object[$keys[$i]].name>><<set $object[' + JSON.stringify($keys[$i]) + '].amount-->><</click>>'>>
<</for>>
The JSON.stringify() bit is there to ensure proper quoting of the key, since we're concatenating a string which will be parsed as markup. Since we know the items we're pre-rendering are strings, we could simply include nested quotes there, however, using JSON.stringify() largely frees you from having to worry about the type of the $variable and the proper way to handle it when doing things like this.
Also note, I only applied the capturing/pre-rendering where necessary. The $keys[$i] which is passed as part of the argument to the <<click>> macro itself doesn't need captured because it gets evaluated as soon as it's printed.
In first passage I set #Inventory1 that displays passage Inventory2 in which I have #Inventory2. When I click on link generated next to an item it doesn't display anything, but shouldn't #Inventory2 already be defined by the above?
Without knowing the context of where this widget is being called, I can't really say—the description doesn't really paint a sufficient picture.
In a passage called Inventory widget is called: <<Food food Eat>> right below it is a div #Inventory1 containing <<display Inventory2>>. Passage Inventory2 contains initially empty div #Inventory2. Objects in $Inventoy are still apples from earlier in this topic.
When I click 1st link(Food) list gets displayed. When I click link generated in the list(Eat)
it runs .effect and reduces .quantity but part that is supposed to replace/display #Inventory1 displays as empty.
Please. The next time someone says descriptions aren't getting the job done, go straight to code. Better yet, start with code. Making others try to figure out what you're doing on top of the issue you need help with is not helpful.
Assuming that <<Food food Eat>> was actually supposed to be <<inventory "Food" "food" "Eat">>. Currently, you have it setup so that your "Eat" links replace "#Inventory1" with the contents of the "Inventory2" passage. The "Inventory2" passage, by your own description, only contains the empty <div id="Inventory2">. So, it's doing exactly what you've told it to do.
May I assume that you're trying to have it redisplay the generated table instead? If so, you probably want something like the following:
You'll note that the widget was split into two widgets to isolate the outer <<click>> from the table generation and replacement, so that the "inventorytable" widget may call itself to update the table. You just use the "inventory" widget as normal.
Also, unless you need it for something else, you no longer need "#Inventory2" as the "#Inventory1" element is sufficient to make this work—though, if you do get rid of it, don't forget to change the target of the <<replace>> at the top of the "inventorytable" widget.
I also corrected a few issues you had with the table itself—nothing serious.
The <div> does keep its contents until replaced, on the page. When you replace it from another passage, however, you get exactly what that passage contains. In other words, replacing the contents of the <div id="Inventory2"> element on the page does not alter how it exists within the "Inventory2" passage, where it's empty.
A handy way to think of passages is as templates. They aren't really "real" except as rendered on the page, and that doesn't alter their non-rendered/template form.
You mean for story options/settings? If so, then in SugarCube 2 the options macros, and their attendant garbage, has been replaced by the wholly superior Setting API, which does have a list-based setting control.
Those SC1 Options Macros were used to add options to the SC1 Options menu link (in SC2 it is Settings) in the side-bar. As TheMadExile stated, he has replaced the related macros with the new Setting API.
If you mean some other sort of options macros then you need to be more specific.
Have a problem with cyclinglink macro not setting a variable (Using SugarCube 2.1.2 with twine 1.4.2 and macro from http://www.motoslave.net/sugarcube/2/).
Correction. You're not trying to set a story variable, you're trying to set a property on an object contained within a story variable.
That's a limitation of the original macro (I believe the <<replacelink>> macros are similar). It cannot modify object properties on story variables, only the base story variable itself.
I don't really have time to offer solutions at the moment. I'll try to get back to this later, or maybe someone else will step up.
Hi, I know this thread has been quietfor a long time, but it seems like your cyclinglink problem hasn't been addressed yet, and I was actually just puzzling out how to do this today! I came across this thread in my hunt for answers, and I thought I should offer some answers back.
This is the macro, with arguments. You should be able to put in as many arguments as you want, but notice the first one. Instead of passing the $senses.smell.desc(description) property, we'll send it 'senses.smell.desc' as a string, and make it call the object property in question later. Just be sure to pass it as a string, and to pass it first, like you would in a <<cyclinglink>>
<<propcycle "senses.smell.desc" "fresh baked bread" "dry summer-grass" "a rocksalt beach" "a muddy meadow" "hot rain on asphalt">>
It's not pretty, but here's the code for the solution. It's a workaround, and I'm not sure about anything other than 1.) it cycles the words in the right order, and 2.) it assigns the currently displayed word to the $senses.smell.desc property
Basically, I'm making liberal use of the ability to print out macros, first to pass in the string for the property we want to change, and assign it the $args[0] value. Then later I assemble to the recursive call, assign the full string to _m, and print that out in replace tags. It's not a cyclinglink, but it looks enough like one to satisfy simple ol' me.
And just to include it, loopback is a macro I wrote so I wouldn't have to keep looking up syntax. It does basically what you'd expect; it swirls an array around.
I'm sure there are better, more informed ways of doing this, but I don't know anything about js, or JSON, so I hope this helps, if of the off-chance anyone can make use of it.
Comments
Is there any way to mix html with variables?
For example:
That said, however, I usually find it cleaner to use alternate quoting characters (single quote vs. double quote), rather than having to escaping the same quotes. For example: It's just a stylistic quibble really.
Here I'm trying to make random discovery/encounter work but apparently visited() doesn't work with disabled history(I think?) and what I'm trying below doesn't work, as usual.
Main passage: Location:
If I understand your request correctly you want to track which passages the reader has visited and your story does not allow the reader to undo a choice.
I see you have a $sewerExplore array storing possible target passage names and a $sewerVisited array tracking which passages have been visited. I am confused by your $visited variable, it appears to be an object with a passage method.
One question, do you want to track every passage the reader has visited in your story, or only ones within a section (Sewers) of the story?
There are two major errors in your Location passage:
1. the ndef operator is used to check if something is defined but you are using it to check the return of the $visited.passage() call, if you are trying to check if the $visited.passage method exists then try removing the parenthesis () it may work.
2. You are trying to assign a number 1 to the $visited.passage() method which is invalid, I don't know exactly what you are trying to do with that <<set>> so I can't offer a solution.
Do you want to track every passage the reader has visited in your story, or only ones within a section (Sewers) of the story?
If you want to track every passage you could do the following:
1. StoryInit special passage: 2. PassageReady special passage: 3. Test if you have been here before: 4. A list of passages visited including the current one:
I tried to avoid using .push outside an if because it would add a new line to object every time that passage is visited when it just needs to be set once.
I guess I will have to resort to manually adding every passage to $visited.
2. Just add the following to the passages you want to track.
Anyway, going on the assumption that $visited is a generic object, I believe something like the following is what is called for, which is just a minor change really. Your main issue was using the dot notation (e.g. $visited.passage()) when you should have been using the square bracket notation (e.g. $visited[passage()]). For example:
Location:
If I may make some suggestions, however:
PassageReady special passage: Location: (tag each of these with sewer)
It seems I didn't explain that sewer is just a test run. Replace it with any other name and you get the idea.
<<set>>: bad evaluation: State.variables.object[State.variables.keys[State.variables.i]] is undefined.
Also tried to sort by amount but I guess I'm doing something wrong there as well.
The effect of what you need to do is somewhat like using a closure in JavaScript.
You're sorting an array of keys, so your compare function is being passed keys, however, you're treating its parameters as if they were objects. For what you want to do, you need to use the keys on $object.
For example (incl. your sort function): The JSON.stringify() bit is there to ensure proper quoting of the key, since we're concatenating a string which will be parsed as markup. Since we know the items we're pre-rendering are strings, we could simply include nested quotes there, however, using JSON.stringify() largely frees you from having to worry about the type of the $variable and the proper way to handle it when doing things like this.
Also note, I only applied the capturing/pre-rendering where necessary. The $keys[$i] which is passed as part of the argument to the <<click>> macro itself doesn't need captured because it gets evaluated as soon as it's printed.
When I click 1st link(Food) list gets displayed. When I click link generated in the list(Eat)
it runs .effect and reduces .quantity but part that is supposed to replace/display #Inventory1 displays as empty.
Assuming that <<Food food Eat>> was actually supposed to be <<inventory "Food" "food" "Eat">>. Currently, you have it setup so that your "Eat" links replace "#Inventory1" with the contents of the "Inventory2" passage. The "Inventory2" passage, by your own description, only contains the empty <div id="Inventory2">. So, it's doing exactly what you've told it to do.
May I assume that you're trying to have it redisplay the generated table instead? If so, you probably want something like the following: You'll note that the widget was split into two widgets to isolate the outer <<click>> from the table generation and replacement, so that the "inventorytable" widget may call itself to update the table. You just use the "inventory" widget as normal.
Also, unless you need it for something else, you no longer need "#Inventory2" as the "#Inventory1" element is sufficient to make this work—though, if you do get rid of it, don't forget to change the target of the <<replace>> at the top of the "inventorytable" widget.
I also corrected a few issues you had with the table itself—nothing serious.
Guess my problem was that I thought div that is replaced will keep its content until it is overwritten.
The <div> does keep its contents until replaced, on the page. When you replace it from another passage, however, you get exactly what that passage contains. In other words, replacing the contents of the <div id="Inventory2"> element on the page does not alter how it exists within the "Inventory2" passage, where it's empty.
A handy way to think of passages is as templates. They aren't really "real" except as rendered on the page, and that doesn't alter their non-rendered/template form.
If you mean some other sort of options macros then you need to be more specific.
That's a limitation of the original macro (I believe the <<replacelink>> macros are similar). It cannot modify object properties on story variables, only the base story variable itself.
I don't really have time to offer solutions at the moment. I'll try to get back to this later, or maybe someone else will step up.
This is the macro, with arguments. You should be able to put in as many arguments as you want, but notice the first one. Instead of passing the $senses.smell.desc(description) property, we'll send it 'senses.smell.desc' as a string, and make it call the object property in question later. Just be sure to pass it as a string, and to pass it first, like you would in a <<cyclinglink>>
It's not pretty, but here's the code for the solution. It's a workaround, and I'm not sure about anything other than 1.) it cycles the words in the right order, and 2.) it assigns the currently displayed word to the $senses.smell.desc property Basically, I'm making liberal use of the ability to print out macros, first to pass in the string for the property we want to change, and assign it the $args[0] value. Then later I assemble to the recursive call, assign the full string to _m, and print that out in replace tags. It's not a cyclinglink, but it looks enough like one to satisfy simple ol' me.
And just to include it, loopback is a macro I wrote so I wouldn't have to keep looking up syntax. It does basically what you'd expect; it swirls an array around.
I'm sure there are better, more informed ways of doing this, but I don't know anything about js, or JSON, so I hope this helps, if of the off-chance anyone can make use of it.