Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Various [SugarCube]

R.R.
edited April 2015 in Help! with 1.x
Started on twine few days ago and got a few questions.

I've been using this to assign points at character creation:
<<if $PC.Strength gt 1>><<click "[←]">><<set $PC.Strength-->><<set $karma++>><<replace "#cc-str">><<print $PC.Strength>><</replace>><<replace "#karma">><<print $karma>><</replace>><<script>>state.display(state.active.title, null, "back")<</script>><</click>><<else>>[←]<<endif>> \
<span id="cc-str"><<print $PC.Strength>></span> \
<<if $karma gte 1 and $PC.Strength lt 18>><<click "[→]">><<set $PC.Strength++>><<set $karma-->><<replace "#cc-str">><<print $PC.Strength>><</replace>><<replace "#karma">><<print $karma>><</replace>><<script>>state.display(state.active.title, null, "back")<</script>><</click>><<else>>[→]<<endif>>
I'd like to know if there is a way to update <<if>> without using state.display(state.active.title, null, "back"). It does the page transition which gets annoying when there are that many clicks and it also resets radio/checkboxes I used for other things on that passage. I wouldn't mind if anyone shared an easier way to do the whole thing either.

How can I use <<return>> to go to last visited passage with a certain tag? I've seen method by Novbert in another topic, just asking if there is a simpler way.

A way to apply <abbr> without having words underlined.

I placed Clock in StoryMenu but that only updates it when passage changes. Is there a way to make it immediately update on any action that changes it(without including it in the action)?

I was also thinking of calculating time something like $time.minutes % 60 and add 1 to $time.hour. Is this possible?

How to append st nd rd th to $time.date (1st, 2nd, etc).

Made array for weekdays and months but not sure how to cycle them...


I don't expect handouts but right now I'm more or less just copy/pasting from various sources and I'm not sure if some of these things are built in and if not whether they should be done in java or CSS.
«1

Comments

  • edited April 2015

    I covered a simpler way to do this here, involving multiple passages and disabling transitions between passages: http://twinery.org/forum/discussion/2625/distributing-stat-values-for-a-player-character#latest

    I don't know how you're going to get persistent radio buttons, but there might be a way to dynamically update page content without reloading the passage. I would probably just redesign the page to not use radio buttons.

  • @Claretta said:
    I covered a simpler way to do this here, involving multiple passages and disabling transitions between passages: http://twinery.org/forum/discussion/2625/distributing-stat-values-for-a-player-character#latest

    I actually based that on your example, though I haven't figured out how to disable transition without disabling it everywhere. I am using span to refresh displayed variables but I wasn't able to use that method on <> part.

  • edited April 2015

    In your css write:


    .passage.notransition
    {
    transition: none;
    -webkit-transition: none;
    -moz-transition: none;
    }

    And then add the tag notransition to the relevant post.

    If that still produces flicker, you can in the previous passage write something like:

    timedcontinue 1s

    addclass ".passage" "notransition"

    ((with << >> around each of those lines, but this forum hates that formatting))

    This will apply the notransition class to the passages before it needs to, so should turn off the transitions before the next passage is called. You may need the replace macro set from the sugarcube website for the timedcontinuie macros.

    to undo that you simply use removeclass in place of addclass.

  • R. wrote:
    I'd like to know if there is a way to update <<if>> without using state.display(state.active.title, null, "back").
    If you're already using <<replace>> to dynamically update content on the page, why are you using state.display() in the first place?


    R. wrote:
    A way to apply <abbr> without having words underlined.
    Use CSS. Something like the following:
    abbr {
    	border-bottom: none;
    }
    

    R. wrote:
    I placed Clock in StoryMenu but that only updates it when passage changes. Is there a way to make it immediately update on any action that changes it(without including it in the action)?
    Wrap the clock display in an element with an ID and update it via <<replace>>. You can wrap the entire update method in a widget to make it easier to use. What does your update process look like?


    As for the other questions, you're going to need to detail what you're actually doing first.
  • Thanks for abbr tip.
    If you're already using <<replace>> to dynamically update content on the page, why are you using state.display() in the first place?

    I'm using if to replace click or else stat could go into negative or exceed its cap. I tried putting whole if into a span and have it replace itself but that only works for a single click.


    Not really related but doesn't warrant another topic:
    I looked at some js tutorials and it seems clock part is more complex than I expected. I meant to make widget or macro to add time on action but I'd have to calculate everything in lowest time unit which is a headache because calculating time would be important mechanism for the game.
    I imagined having objects age, for example creating $apple and adding age, so at 0 it would be fresh and once reaching certain age it would become rotten. Another idea was to have say, training weights and while character has it equipped it would increase strength over time up to certain point.

    Of course, my knowledge of js and css is almost non-existent. Guess I'll have to do more research.
  • R. wrote: »
    I'm using if to replace click or else stat could go into negative or exceed its cap. I tried putting whole if into a span and have it replace itself but that only works for a single click.
    If you simply need the buttons to enforce limits, then you could move the logic inside the <<click>> macros. For example:
    <<click "[←]">><<if $PC.Strength gt 1>><<set $PC.Strength-->><<set $karma++>><<replace "#cc-str">><<print $PC.Strength>><</replace>><<replace "#karma">><<print $karma>><</replace>><</if>><</click>> \
    <span id="cc-str"><<print $PC.Strength>></span> \
    <<click "[→]">><<if $karma gte 1 and $PC.Strength lt 18>><<set $PC.Strength++>><<set $karma-->><<replace "#cc-str">><<print $PC.Strength>><</replace>><<replace "#karma">><<print $karma>><</replace>><</if>><</click>>
    
    Now, that won't disable the buttons, but it accomplishes your primary goal of enforcing ranges on the stats and limiting increases by available karma.

    That said, if you're married to the idea of disabling the buttons, then you could place all of the stat modification code in another passage and (re)display the whole thing via a <<display>> inside a <<replace>>. The effect is similar to what you're doing now, except without the unnecessary passage navigation. As a beneficial side-effect, it also reduces both the number of target elements to one and instances of <<replace>> macros. Example in the attached TWS (which has been ZIP archived, because TWS files aren't currently allowed).
  • (Whoops -- .tws files should now work.)
  • If you simply need the buttons to enforce limits, then you could move the logic inside the <<click>> macros. For example:
    <<click "[←]">><<if $PC.Strength gt 1>><<set $PC.Strength-->><<set $karma++>><<replace "#cc-str">><<print $PC.Strength>><</replace>><<replace "#karma">><<print $karma>><</replace>><</if>><</click>> \
    <span id="cc-str"><<print $PC.Strength>></span> \
    <<click "[→]">><<if $karma gte 1 and $PC.Strength lt 18>><<set $PC.Strength++>><<set $karma-->><<replace "#cc-str">><<print $PC.Strength>><</replace>><<replace "#karma">><<print $karma>><</replace>><</if>><</click>>
    
    Now, that won't disable the buttons, but it accomplishes your primary goal of enforcing ranges on the stats and limiting increases by available karma.

    That said, if you're married to the idea of disabling the buttons, then you could place all of the stat modification code in another passage and (re)display the whole thing via a <<display>> inside a <<replace>>. The effect is similar to what you're doing now, except without the unnecessary passage navigation. As a beneficial side-effect, it also reduces both the number of target elements to one and instances of <<replace>> macros.

    Thanks, that is exactly what I was trying to make it do.
  • I have yet another question. Working on time I set it up like this:
    <<set $time = { Year: 5, Month: 5, Date: 5, Day: 5, Hour: 5, Minute: 5, }>>
    <<set $day to ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday",]>>
    
    and I can do print $day[0]. How would I get it to work like this:
    <<print $day[$time.Day]>>
    
  • Javascript arrays are zero based (the first element has an index of zero) so if the Day: 5 part of your example is meant to represent Friday then you just need to subtract one from its value.
    <<print $day[$time.Day-1]>>
    
  • greyelf wrote: »
    Javascript arrays are zero based (the first element has an index of zero) so if the Day: 5 part of your example is meant to represent Friday then you just need to subtract one from its value.
    <<print $day[$time.Day-1]>>
    
    Huh. I asked the wrong question(for some reason I couldn't get it to print $day[$time.day] but today it works) but still got a useful answer.

    Today I went and created a passage
    <<nobr>>
    <<if $time.Minute gte 60>><<set $time.Hour += (Math.trunc($time.Minute / 60))>><<set $time.Minute to ($time.Minute % 60)>><</if>>
    <<if $time.Hour gte 24>><<set $time.Day += (Math.trunc($time.Hour / 24));$time.Week += (Math.trunc($time.Hour / 24))>><<set $time.Hour to ($time.Hour % 24)>><</if>>
    <<if $time.Day gt $time.MonthDays>><<set $time.Month += (Math.trunc($time.Day / $time.MonthDays))>><<set $time.Day to (($time.Day % $time.MonthDays)+1)>><</if>>
    
    <<if $time.Week gt 7>><<set $time.Week to ($time.Week % 7)>><</if>>
    <<if $time.Month gt 12>><<set $time.Month -= 12;$time.Year += 1>><</if>>
    
    <<if $time.Month eq 1>><<set $time.MonthDays to 31 >><</if>>
    <<if $time.Month eq 2>><<set $time.MonthDays to 28 >><</if>>
    <<if $time.Month eq 3>><<set $time.MonthDays to 31 >><</if>>
    <<if $time.Month eq 4>><<set $time.MonthDays to 30 >><</if>>
    <<if $time.Month eq 5>><<set $time.MonthDays to 31 >><</if>>
    <<if $time.Month eq 6>><<set $time.MonthDays to 30 >><</if>>
    <<if $time.Month eq 7>><<set $time.MonthDays to 31 >><</if>>
    <<if $time.Month eq 8>><<set $time.MonthDays to 31 >><</if>>
    <<if $time.Month eq 9>><<set $time.MonthDays to 30 >><</if>>
    <<if $time.Month eq 10>><<set $time.MonthDays to 31 >><</if>>
    <<if $time.Month eq 11>><<set $time.MonthDays to 30 >><</if>>
    <<if $time.Month eq 12>><<set $time.MonthDays to 31 >><</if>>
    <</nobr>>
    
    <<print $day[$time.Week-1]>>
    <<print $month[$time.Month-1]>>
    Day:<<print $time.Day>>
    Month:<<print $time.Month>>
    Year:<<print $time.Year>>
    <<if $time.Hour gte 0 and $time.Hour lte 9>>0<</if>><<print $time.Hour>>\
    :<<if $time.Minute gte 0 and $time.Minute lte 9>>0<</if>><<print $time.Minute>>
    
    It works mostly as I intended but when its saturday and I advance time by 8 days result is 0 and nothing gets displayed.
    <<if $time.Week gt 7>><<set $time.Week to ($time.Week % 7)>><</if>>
    
    Tried adding if 0 set to 1 but then result is monday.

    Also, will using this method for time bite me in the ass when I try to make something to calculate object age and effects over time?
  • I couldn't get it to print $day[$time.day]
    Javascript is case sensitive, therefor so is TwineScript.
    <<set $time = { Year: 5, Month: 5, Date: 5, Day: 5, Hour: 5, Minute: 5 }>>
    
    correct: <<print $day[$time.Day]>>
    incorrect: <<print $day[$time.day]>>
    

    Is there a reason you are building your own date handling instead of using Javascript's built-in Date class?
    <<set $now to new Date(Date.now())>>
    now: <<print $now>>
    year: <<print $now.getYear()>>
    month: <<print $now.getMonth()>>
    day: <<print $now.getDay()>>
    hours: <<print $now.getHours()>>
    minutes: <<print $now.getMinutes()>>
    seconds: <<print $now.getSeconds()>>
    

    You can even extend the Date class so that it can return day / month names.
    Place the following into your Story Javascript:
    if (typeof Date.prototype.getDayName === "undefined") {
    	Date.prototype.getDayName = function() {
    		return ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][this.getDay()];
    	};
    	
    	Date.prototype.getMonthName = function() {
    		return ['January','February','March','April','May','June','July','August','September','October','November','December'][this.getMonth()];
    	};
    }
    
    And then the following will work:
    day name: <<print $now.getDayName()>>
    month name: <<print $now.getMonthName()>>
    
  • greyelf wrote: »
    Is there a reason you are building your own date handling instead of using Javascript's built-in Date class?
    I've only read whats on twine wiki and motoslave and I was thinking in twine.

    Tried that method and when I
    <<set $now to new Date(2017, 1, 8, 12, 42)>>
    
    month comes out as february.
  • You may of missed the link to the Date class documentation I included in my previous post.

    The month value range is from 0 for January to 11 for December.

    A story generated using Twine is basically a HTML web-application that contains a javascript engine that creates the story pages/passages as they are needed, these pages are styled using CSS.

    To get full usage from Twine it helps to know some CSS to make your story look the way you want, some HTML to structure the content, and maybe even some Javascript if you want to do non-basic things.
  • The month parameter is 0-based (as noted in the MDN Date docs linked by greyelf). So, by specifying 1, you did specify February.

    Assuming you were shooting for the date January 08 2017 12:42:00 (local time), then what you wanted was the following:
    <<set $now to new Date(2017, 0, 8, 12, 42)>>
    
    Alternatively, you could instead supply a full, or partial, ISO 8601date string, if that would be easier. For example, the same date using a partial date string:
    <<set $now to new Date("2017-01-08T12:42")>>
    
  • After mucking about for a while I came up with this to advance time:
    <<widget "time">>
    <<set $before to $now>>
    <<if $args[0] eq "min">><<set $now.addMinutes($args[1])>>
    <<elseif $args[0] eq "hrs">><<set $now.addHours($args[1])>>
    <<elseif $args[0] eq "days">><<set $now.addDay($args[1])>>
    <<elseif $args[0] eq "month">><<set $now.addMonth($args[1])>>
    <</if>><<replace "#calendar">><<display "Time">><</replace>><</widget>>
    

    When I took a look at making time pass topic I got idea to make object like this:
    <<set $apple= {
    name: "Apple",
    description1: "Fresh apple",
    description2: "Rotten apple",
    health: 30,
    rateofdecay: 2,
    size: 1,
    weight: 1,
    type: "Food",
    effect: "",
    }>>
    
    so it would work something like $apple.health - (($now - $before) * $apple.rateofdecay).

    Except I'd want it to affect every object that has .health and .rateofdecay. Though I should probably somehow limit it to player inventory array or every apple in game would become rotten.
    Anyway, my question is how to affect every object with a specific property?

    Second, how can I target specific property of object, for example, displaying tooltip with $apple.description on mouseover?

    And last, how can I make a top bar that would work like the sugarcube side bar does?
  • edited May 2015
    That seems rather complex to just track the state of apples. Twine games are generally turn-based, so they can track time with much simpler turn-based mechanics.

    Couldn't you just set a normal variable to decrease at certain times and use that to determine the status of an $applehealth variable?
  • R.R.
    edited May 2015
    Claretta wrote: »
    That seems rather complex to just track the state of apples. Twine games are generally turn-based, so they can track time with much simpler turn-based mechanics.

    Couldn't you just set a normal variable to decrease at certain times and use that to determine the status of an $applehealth variable?
    What I'm trying to make is closer to simulation rather than a straightforward game, user would operate from hub passages where events would appear depending on time, relationship with NPCs, PC's abilities and previous events.

    As for apple, wouldn't that work only if there was just one apple object? How would I track 2 apples that were added to array at different times? Besides, apple was just an example. Hm, maybe if I included a for and if $target.rateofdecay in a time widget...
  • edited May 2015
    Unless you're making a game about apple farming with hundreds of apples, I'd probably just track each one individually.

    It'd be simple enough to do something like:

    <<set $heldapple1 to 5>> , <<set $heldapple2 to 5>> etc, and decrease 1 from $heldapple$1, 2, 3, 4, 5 etc every time the timer advances. When they get to 0, they get replaced by rotten apples.

    If you're only expecting to have the player carry a limited amount of apples, it seems the simplest way, and while the code takes a few extra lines and variables, you could write it in a fraction of the time you needed to learn how to make another way work.


    For your other question: http://stackoverflow.com/questions/7503183/what-is-the-easiest-way-to-create-an-html-mouse-over-tool-tip

    http://sixrevisions.com/css/css-only-tooltips/
  • Decided to move on from decay mechanic. Made an array $invMisc[] and pushed few objects like the apple above into it. Now when I try
    <<click "Misc">>
    <<for $i to 0; $i lt $invMisc.length; $i++>>
    <<print $invMisc[$i].name>>
    <</for>><</click>>
    
    nothing happens. To clarify, I wanted it to print name property of each object in array. What am I doing wrong?
  • The <<click>> macro doesn't do replacement on its own. You'll also need to use the <<replace>> macro.

    Try something like the following, to replace the contents of a nearby element:
    <<click "Misc">>
    <<replace "#inv-misc">>\
    <<for $i to 0; $i lt $invMisc.length; $i++>>\
    <<print $invMisc[$i].name>>\
    <<if $i gt 0>>, <</if>>\
    <</for>>\
    <</replace>>
    <</click>>: <span id="inv-misc"></span>
    
    Or the following, to replace the the Misc link itself:
    <span id="inv-misc"><<click "Misc">>
    <<replace "#inv-misc">>\
    <<for $i to 0; $i lt $invMisc.length; $i++>>\
    <<print $invMisc[$i].name>>\
    <<if $i gt 0>>, <</if>>\
    <</for>>\
    <</replace>>
    <</click>></span>
    
  • The <<click>> macro doesn't do replacement on its own. You'll also need to use the <<replace>> macro.
    Thanks, that works, sort of. Using that example when I open a different passage, then returning and clicking doubles the list.
  • I'm back. Again.
    Using this for displaying inventory:
    <<click "Inventory">>
    <<replace "#inv-misc">>
    <<for $i to 0, $l to $invMisc.length; $i lt $l; $i++>>\
    <<if $invMisc[$i].quantity gte 1>><<popup $invMisc[$i].name "InvPopup">><<print $invMisc[$i].quantity>><br><<endif>>\
    <</for>>\
    <</replace>>\
    <</click>><br><span id="inv-misc"></span>
    
    Filled array with couple of objects and then used
    <<set $invMisc[$yellowApple.quantity] to 3>>
    
    to change value.
    First time I used it it worked as intended. Then I accidentally hit ctrl Z instead of T in the main window(damn you qwertz!) and had to write it again. Except now it just overwrites the first object in array with that value instead of objects quantity value.
  • R. wrote: »
    Filled array with couple of objects and then used
    <<set $invMisc[$yellowApple.quantity] to 3>>
    
    to change value.
    First time I used it it worked as intended. Then I accidentally hit ctrl Z instead of T in the main window(damn you qwertz!) and had to write it again. Except now it just overwrites the first object in array with that value instead of objects quantity value.
    Assuming I'm understanding what you're doing, you're either going to need to iterate over $invMisc until you find the correct object or have the object's index within the array cached someplace.

    If $yellowApple is the cached index (I'm going to assume it is), then you've simply got .quantity in the wrong place. It goes outside the square brackets, like so:
    <<set $invMisc[$yellowApple].quantity to 3>>
    /*
    	$invMisc[$yellowApple]  ← Returns the object from the array.
    	.quantity               ← Accesses the the object's "quantity" property.
    */
    
  • Right. Thought I did it wrong when error popped up.
    I set the $yellowApple object, push it into array and can see it with checkvars macro.
    $invMisc	[ 0: { name: "Yellow apple", description: "Juicy yellow apple", type: "Food", effect: "", quantity: 0 } ]
    
    But when I try to set quantity with click macro
    <<set>>: bad evaluation: state.active.variables.invMisc[state.active.variables.yellowApple] is undefined.
    
  • What does $yellowApple hold? I said that I was assuming it was the index, though apparently it's not.

    Does it hold a reference to the object within the array itself? If so, then the above isn't going to work. You'd simply do something like:
    <<set $yellowApple.quantity to 3>>
    
    That works since it's a reference to the object within the array (i.e. $yellowApple and $invMisc[ /* "Yellow apple" index */ ] both refer to the same object). See the warning below.

    If it's not a reference, then as I said before, you either need to have the index on hand or you'll have search for it.

    Reference Caution
    Because of the way most story formats work, references in story $variables will be broken each "turn" (i.e. each time you navigate passages). So, you should probably only use references in cases where you can recreate the reference each turn and doing so isn't onerous.

    In most cases, and especially if you need to maintain a "reference" to an array member or object property across turns, what you should do is record the index and/or property name of the thing you wish to access rather than a reference to it.
  • Its just an object.
    <<set $yellowApple = {
    name: "Yellow apple",
    description: "Juicy yellow apple",
    type: "Food",
    effect: "",
    quantity: 0,
    }>>
    <<set $invMisc to [] >>
    
    <<set $invMisc.push($yellowApple)>>
    
  • Then (as noted above) within the passage you push the object referenced within $yellowApple onto $invMisc, both refer to the same object, so changing one changes the other. Within any other navigated passage, however, they will be equivalent but separate objects, so you can no longer modify the properties of the yellow apple object in $invMisc by changing those in $yellowApple. You will have to either record the index of the yellow apple object when you push it onto the array (which mostly defeats the purpose of using an array in the first place, by requiring you to keep track of scores of indices), or search $invMisc for its yellow apple object before you can modify it.

    For example, search and set using <<for>>:
    <<for $i to 0; $i lt $invMisc.length; $i++>>\
    <<if $invMisc[$i].name is "Yellow apple">><<set $invMisc[$i].quantity to 3>><</if>>\
    <</for>>
    
    As you can see, that's pretty cumbersome. Alternatively, you could use <Array>.find() if you don't mind a little JavaScript:
    <<set $invMisc.find(function (obj) { return obj.name is "Yellow apple" }).quantity to 3>>
    
    That could be made shorter by being wrapped up within a utility function.


    If you're going to be doing that a lot, you may be better off using an object to hold your inventory, rather than an array. For example:
    <<set $invMisc to {}>>
    
    <<set $invMisc["YellowApple"] to {
    	name        : "Yellow apple",
    	description : "Juicy yellow apple",
    	type        : "Food",
    	effect      : "",
    	quantity    : 0,
    }>>
    <<set $invMisc["GreenApple"] to {
    	name        : "Green apple",
    	description : "Juicy green apple",
    	type        : "Food",
    	effect      : "",
    	quantity    : 0,
    }>>
    <<set $invMisc["RedApple"] to {
    	name        : "Red apple",
    	description : "Juicy red apple",
    	type        : "Food",
    	effect      : "",
    	quantity    : 0,
    }>>
    

    Then, later on, changing the quantity of yellow apples would simply be:
    <<set $invMisc["YellowApple"].quantity to 3>>
    

    Printing out the inventory would require a few changes, but nothing too severe, for example:
    <<click "Inventory">>
    <<replace "#inv-misc">>
    <<for $i to 0, $keys to Object.keys($invMisc).sort(); $i lt $keys.length; $i++>>\
    <<if $invMisc[$keys[$i]].quantity gte 1>>\
    <<popup $invMisc[$keys[$i]].name "InvPopup">><<print $invMisc[$keys[$i]].quantity>><br>\
    <</if>>\
    <</for>>\
    <</replace>>\
    <</click>><br><span id="inv-misc"></span>
    
  • Thanks TheMadExile, that really helped(sorry for late thanks but I didn't want to bump before I had something else to ask).

    Now, the question.

    How can I store function inside object, like this:
    var object = {
        a: 1,
        b: 2,
        c: function () {
            return this.a + this.b;}
    }
    
    Tried defining whole object in JS and while I don't get any errors testing it, I don't see it with checkvars. And I can't figure out how to do the same in twine.
  • Preface: I do not, generally, recommend storing objects which are not simply property bags (i.e. those containing methods, including getters/setters) in story $variables. You can do so, but it bloats the serializations (used in the session store and saves) a bit, which can be worked around, but that's a somewhat advanced topic. Simple objects are no problem.

    The <<checkvars>> debugging macro only shows story $variables, so you're not going to see JavaScript variables, if that's what you were trying.

    Anyway, the code you have there is correct, and one way to attach a method to an object.

    As to how to do it in Twine, which I assume means how to do it with story $variables, is exactly the same. For example:
    <<set $obj to {
    	a : 1,
    	b : 2,
    	c : function () {
    		return this.a + this.b;
    	}
    }>>
    <<print $obj.c()>>/* Should print: 3 */
    
Sign In or Register to comment.