Howdy, Stranger!

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

Call an object method with a widget argument

I have several things such as the height and hair color that the player can set about the PC from a pre-defined list. Those lists use a basic setup. They are a link that replaces an above span with a phrase describing what they've picked. Basically it's getting to the point of being somewhat hard to figure out what I'm looking at, and I'd like to make all that into a widget. I believe the following should mostly work.
<<widget "pcConfigLink">><<link $args[0]>><<set $playerCharacter.$args[1] = $args[2]>><<replace ".pc"+$args[1]>><<pc+$args[1]>><</replace>><</link>><</widget>><<widget "pcConfigLink">><<link $args[0]>><<set $playerCharacter.$args[1] = $args[2]>><<replace ".pc"+$args[1]>><<pc+$args[1]>><</replace>><</link>><</widget>>

I would be able to call it for example with:
<<pcConfigLink "Towering" Height 4>>

However it's not liking $playerCharacter.$args[1] for some reason. Any suggestion on how to get this working?

Comments

  • edited May 2017
    Try using bracket notation. Dot notation doesn't work with variables.
  • I'm going to assume you're using a recent version of SugarCube v2. You really should specify the story format and its full version.


    You're attempting to use dot notation, which is only for specific property access using an identifier. You need to use square bracket notation. For example:
    $playerCharacter[$args[1]]
    

    Beyond that, you cannot generally pass an expression in an argument slot as you're attempting to with <<replace>>. If you need to use an expression in an argument slot, you'll have to use the backtick expression syntax. For example:
    <<replace `".pc" + $args[1]`>>
    
    Note: Posting from my phone, hopefully the backticks come through.

    Also, I'm unsure what you're attempting to do with the following:
    <<pc+$args[1]>>
    
    It's probably not going to do whatever it is you're hoping it will.
  • Sorry, just had surgery yesterday, and they have me on Vicodin. I have Twine up to date and I presume it's keeping SugarCube v2 up to date for me. What I'm doing is:
    You have <span class="pcHairLength"><<pcHairLength>></span> <span class="pcHairType"><<pcHairType>></span> <span class="pcHairColor"><<pcHairColor>></span> hair.
    <<link "Shaved">><<set $playerCharacter.HairLength = 0>><<replace ".pcHairLength">><<pcHairLength>><</replace>><</link>> | <<link "Ear-Length">><<set $playerCharacter.HairLength = 1>><<replace ".pcHairLength">><<pcHairLength>><</replace>><</link>> | <<link "Shoulder-Length">><<set $playerCharacter.HairLength = 2>><<replace ".pcHairLength">><<pcHairLength>><</replace>><</link>> | <<link "Navel-Length">><<set $playerCharacter.HairLength = 3>><<replace ".pcHairLength">><<pcHairLength>><</replace>><</link>> | <<link "Waist-Length">><<set $playerCharacter.HairLength = 4>><<replace ".pcHairLength">><<pcHairLength>><</replace>><</link>> | <<link "Hip-Length">><<set $playerCharacter.HairLength = 5>><<replace ".pcHairLength">><<pcHairLength>><</replace>><</link>> | <<link "Knee-Length">><<set $playerCharacter.HairLength = 6>><<replace ".pcHairLength">><<pcHairLength>><</replace>><</link>> | <<link "Ankle-Length">><<set $playerCharacter.HairLength = 7>><<replace ".pcHairLength">><<pcHairLength>><</replace>><</link>> | <<link "Floor-Length">><<set $playerCharacter.HairLength = 8>><<replace ".pcHairLength">><<pcHairLength>><</replace>><</link>>
    
    <<link "Straight">><<set $playerCharacter.HairType = 0>><<replace ".pcHairType">><<pcHairType>><</replace>><</link>> | <<link "Wavy">><<set $playerCharacter.HairType = 1>><<replace ".pcHairType">><<pcHairType>><</replace>><</link>> | <<link "Curly">><<set $playerCharacter.HairType = 2>><<replace ".pcHairType">><<pcHairType>><</replace>><</link>>
    
    <<link "Blonde">><<set $playerCharacter.HairColor = 0>><<replace ".pcHairColor">><<pcHairColor>><</replace>><</link>> | <<link "Strawberry Blonde">><<set $playerCharacter.HairColor = 1>><<replace ".pcHairColor">><<pcHairColor>><</replace>><</link>> | <<link "Red">><<set $playerCharacter.HairColor = 2>><<replace ".pcHairColor">><<pcHairColor>><</replace>><</link>> | <<link "Copper">><<set $playerCharacter.HairColor = 3>><<replace ".pcHairColor">><<pcHairColor>><</replace>><</link>> | <<link "Chestnut">><<set $playerCharacter.HairColor = 4>><<replace ".pcHairColor">><<pcHairColor>><</replace>><</link>> | <<link "Brown">><<set $playerCharacter.HairColor = 5>><<replace ".pcHairColor">><<pcHairColor>><</replace>><</link>> | <<link "Black">><<set $playerCharacter.HairColor = 6>><<replace ".pcHairColor">><<pcHairColor>><</replace>><</link>>
    
    Under widgets I have:
    <<widget "pcHairLength">><<switch $playerCharacter.HairLength>>
    <<case 0>>shaved
    <<case 1>>ear-length
    <<case 2>>shoulder-length
    <<case 3>>navel-length
    <<case 4>>waist-length
    <<case 5>>hip-length
    <<case 6>>knee-length
    <<case 7>>ankle-length
    <<case 8>>floor-length
    <</switch>><</widget>>
    
    <<widget "pcHairType">><<switch $playerCharacter.HairType>>
    <<case 0>>straight
    <<case 1>>wavy
    <<case 2>>curly
    <</switch>><</widget>>
    
    <<widget "pcHairColor">><<switch $playerCharacter.HairColor>>
    <<case 0>>blonde
    <<case 1>>strawberry blonde
    <<case 2>>red
    <<case 3>>copper
    <<case 4>>chestnut
    <<case 5>>brown
    <<case 6>>black
    <</switch>><</widget>>
    

    Basically I'm trying to replace the upper bit of code with a widget. (I also have things like Height, Race, etc.) In an attempt to make it a bit more readable.

    I've rewritten the link thing to (and it still fails, but not as terribly):
    <<widget "pcConfigLink">><<link $args[0]>><<set _widget = "pc"+$args[1]>><<set _class = "."+_widget>><<set $playerCharacter[$args[1]] = $args[2]>><<replace _class>><<_widget>><</replace>><</link>><</widget>>
    
    The replace works, but the widget call does not. To test, I added this to end of the HairType list:
    <<pcConfigLink "Curly" "HairType" 2>>
    
  • I have Twine up to date and I presume it's keeping SugarCube v2 up to date for me.
    While Twine v2.1.3 does come with the current latest (v2.18.0) version of SugarCube 2, it also comes with SugarCube v1.0.35 (as well as two versions of Harlowe and one of Snowman 2).

    The syntax of your example allows us to guess that you are using one of the SugarCube series (1.x or 2.x) but without knowing the exact version of the story format (or of the Twine 2 application) we can't know what features are available to you (and to us) to use in a solution.

    warning: The Twine 2 application does not automatically keep you up to date with the latest releases of SugarCube (1.x or 2.x) or any other story format. That is either left up to you to do yourself manually via the "Add Story Format" option, or it may happen when you install/use a newer release/version of the Twine 2 application.
  • It's 2.18.0 that I'm using. I tried Harlowe, but didn't quite like it. I actually did presume that it was keeping SugarCube up to date for me.
  • edited May 2017
    Having widgets just to print values is a waste; I think you should just use variables for that. In fact, your widgets don't appear to be doing much for you other than muddying the water. Plus you're trying to trick the parser with a variable trying to call a widget. This is all very complicated, and it's going to wind up turning into a mess if you aren't careful.

    Why not something array-based, like this:
    You have <span class="pcHairLength">$playerCharacter.HairLength</span> <span class="pcHairType">$playerCharacter.HairType</span> <span class="pcHairColor">$playerCharacter.HairColor</span> hair.
    
    <<set _hairLength to ['Shaved', 'Ear-Length', 'Shoulder-Length', 'Navel-Length', 'Waist-Length', 'Hip-Length', 'Knee-Length', 'Ankle-Length', 'Floor-Length']>>\
    <<for _i to 0; _i < _hairLength.length; _i++>>\
        <<capture _i>>\
            <<link _hairLength[_i]>>
    
                <<set $playerCharacter.HairLength to _hairLength[_i].toLowerCase()>>
    
                <<replace ".pcHairLength">>\
                    $playerCharacter.HairLength\
                <</replace>>
    
            <</link>>
        <</capture>>\
    <</for>>\
    
    ...and so on for the others.
    

    I think this is a better way to accomplish what I think you're trying to do:

    1. You can increase the number of options just by adding a value to the array--no other code needs rewritten.

    2. All the code is right there in the passage.

    3. No unnecessary macro/widget calls.

    4. It's much shorter, more readable, and easier to debug.

    5. You could pretty easily turn the above code into a widget for reuse, and you wouldn't have to hard code individual widgets for each player attribute.

    Here's an example of how it might work as a widget:
    ::in a widget tagged passage
    <<widget 'setAttribute'>>\
        <<nobr>>
            <<set _opts to $args[0]>>
            <<set _attr to $args[1]>>
            <<set _class to '.pc' + _attr>>
            <<for _i to 0; _i < _opts.length; _i++>>
                <<capture _i>>
                    <<link _opts[_i]>>
    
                    <<set $playerCharacter[_attr] to _opts[_i].toLowerCase()>>
    
                    <<replace _class>>\
                        $playerCharacter[_attr]\
                    <</replace>>
    
                    <</link>>
                <</capture>>
            <</for>>
        <</nobr>>\
    <</widget>>
    
    ::in another passage
    <<set _hairLength to ['Shaved', 'Ear-Length', 'Shoulder-Length', 'Navel-Length', 'Waist-Length', 'Hip-Length', 'Knee-Length', 'Ankle-Length', 'Floor-Length']>>\
    <<setAttribute _hairLength 'HairLength'>>
    

    Note: you should try spacing and indenting your code to make it more readable.
  • I am basically treating them like functions. I'd rather not have to list the array everywhere I use it. Specifically that's what I'm trying to prevent, is to have the list once as a widget, and then everywhere that I describe the player's hair I can just call the widget to display what it's set to. I'm only going to configure it in a small number of places though. If I could get my pcConfigLink widget working I'd have it all done. The only issue I have left is when I have:
    <<_widget>>
    
    it replaces it with the widget name, not the widget. So
    <<pcHairLength>>
    
    If I could get it to call the widget instead of printing that it'd be done.
  • edited May 2017
    I am basically treating them like functions. I'd rather not have to list the array everywhere I use it.

    I think I understand what you're trying to do. What I don't understand is why you're trying to trick the parser into turning the variable into a widget call and why you're trying to use three different macro calls to accomplish the same thing a variable does. There are better solutions.

    If you don't like the array method, I'm not gonna evangelize for it. I do wish that you had at least tested the widget or code I provided so you could see what it does.
    If I could get it to call the widget instead of printing that it'd be done.

    If you're committed to trying to force the issue, you could just trick the parser using <<print>>:
    <<print '<<' + _widget + '>>'>>
    

    Keep in mind, if you need trick the parser, then you need to write better code 99% of the time. Your case is not an exception to this rule.
  • Chapel wrote: »
    I think I understand what you're trying to do. What I don't understand is why you're trying to trick the parser into turning the variable into a widget call and why you're trying to use three different macro calls to accomplish the same thing a variable does. There are better solutions.

    I'm open to better solutions, and I did read your code to see what it did, it's not a bad solution, more what you provided is the opposite of what I'm trying to accomplish. For the most part I'm just going to use something like:
    Your <<pcHairColor>> hair got wet.
    
    It's specifically in the few places where I'm letting the player choose from the list of everything that I'm trying to be a bit too fancy. The main reason I'm not including the whole code is that it's an adult game, and I'm not sure whether that's allowable to be linked on this forum.

    The reason I am trying to basically wrap the link/replace code in a widget is to make it a bit easier to read. I'd rather not trick the parser, if it doesn't work how I want it to, then I have to rethink what I'm trying to do.
  • edited May 2017
    I'm unsure if I'd go so far as to call it tricking the parser, however, it is true that you cannot currently use a variable to directly invoke a macro, widget macro or otherwise. The parser simply isn't written to allow that.

    You have two possible solutions, other than doing something else completely, either use what's known as the Stupid Print Trick™, which has already been presented to you, or use an intermediate routing widget.

    Personally, while I generally advise against use of the Stupid Print Trick™ whenever possible, your case is simple enough that I'd just go with it. For example:
    <<widget "pcConfigLink">>\
    <<link $args[0]>>
    	<<set $playerCharacter[$args[1]] = $args[2]>>
    	<<set _name = "pc" + $args[1]>>
    	<<replace `"." + _name`>><<="<<" + _name + ">>">><</replace>>
    <</link>>\
    <</widget>>
    
    Beyond that, if the element you're replacing the contents of will be unique—probably the case here—then use an ID instead of a class.
  • edited May 2017
    I may just be completely misunderstanding you. I don't get how this is the opposite of what you're asking for.

    I don't get why you want the widgets to return the strings instead of a variable. It's a more difficult, more taxing, less flexible way of doing thing.

    What I was thinking was something like this--you still only ever need to write the player's options once. You don't have to mess around with numbers and save those to the $playerCharacter properties. You don't have to trick the parser, and you don't have to right tons of code.

    Of course, none of it matters if I'm missing the point, which I very well might be. I attached a demo so you can see what I mean without having to copy/paste it all over. Note that I missed a dollar sign at the end of it, and it didn't save right, so the 'next' passage is has an error.
    ::StoryInit
    <<set $hairLength to [
        'Shaved',
        'Ear-Length',
        'Shoulder-Length',
        'Navel-Length',
        'Waist-Length',
        'Hip-Length',
        'Knee-Length',
        'Ankle-Length',
        'Floor-Length'
    ]>>
    
    <<set $hairType to [
        'Curly',
        'Wavy',
        'Straight'
    ]>>
    
    <<set $hairColor to [
        'Brown',
        'Blonde',
        'Red'
    ]>>
    /% this is the only time you'll have to write out your options. %/
    <<set $pc = {
    	HairColor  : '',
    	HairLangth : '',
    	HairType   : ''
    }>>
    
    <<set $pc.HairColor to $hairColor.random().toLowerCase()>>
    <<set $pc.HairLength to $hairLength.random().toLowerCase()>>
    <<set $pc.HairType to $hairType.random().toLowerCase()>>
    
    ::widgets [widget]
    <<widget 'setAttribute'>>\
        <<set _opts  to $args[0]>><<capture _opts>>\
            <<set _attr  to $args[1]>>\
    		<<set _class to '.pc' + _attr>><<capture _class>>\
    			<<for _i to 0; _i < _opts.length; _i++>>\
    				<<capture _i>>\
    					<<link _opts[_i]>>
    
    					<<set $pc[_attr] to _opts[_i].toLowerCase()>>
    
    					<<replace _class>>\
    						$pc[_attr]\
    					<</replace>>
    
    					<</link>>
    				<</capture>>\
    			<</for>><</capture>><</capture>>
    <</widget>>
    /% note how many captures I missed last time.  I should've tried it with multiple options %/
    
    ::Start
    You have <span class="pcHairLength"><<print $pc.HairLength>></span> <span class="pcHairType"><<print $pc.HairType>></span> <span class="pcHairColor"><<print $pc.HairColor>></span> hair.
    
    <<setAttribute $hairLength 'HairLength'>>
    <<setAttribute $hairColor 'HairColor'>>
    <<setAttribute $hairType 'HairType'>>
    
    [[next]]
    
    ::next
    You have $pc.HairColor hair.  It's $pc.HairType.
    
    [[Start]]
    
  • With the print trick working, it seems to be solid now. Thank you both. I think we just come at it from different points is all Chapel.
  • edited May 2017
    With the print trick working, it seems to be solid now. Thank you both. I think we just come at it from different points is all Chapel.

    The important thing is that you got it working how you want. I think I just somehow missed the whole point of this, so that's on me, sorry for adding more confusion to everything.
Sign In or Register to comment.