Howdy, Stranger!

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

Using Custom macros with Twine macros (Change case macro)

edited February 2014 in Help! with 1.x
I am trying to teach myself how to write custom macros in Twine, and decided to start with something a bit simpler than this problem here, so I figured a case macro might be quite handy as there doesn't appear to be anything built into Twine for this. 

Using the code posted by TheMadExile here, I decided to try and adapt it, and I do appear to have something that seems to work for pieces of text.

Code:
Upper : <<changecase "upper">>tHisIsAtEsT<<endchangecase>>

Lower : <<changecase "lower">>tHisIsAtEsT<<endchangecase>>

Proper : <<changecase "proper">>tHisIsAtEsT<<endchangecase>>

Incorrect use: <<changecase>>tHisIsAtEsT<<endchangecase>>


Script:
//casemacro allows user to change text case to uppercase, lowercase or proper/titlecase.
//usage examples:
// <<changecase "upper">>text<<endchangecase>>
// <<changecase "lower">>text<<endchangecase>>
// <<changecase "proper">>text<<endchangecase>>


version.extensions["caseMacro"] = { major: 1, minor: 0, revision: 0 };
macros["changecase"] =
{
handler: function(place, macroName, params, parser)
{
if (params.length === 0)
{
throwError(place, "<<" + macroName + ">>: Please specify case type i.e. upper, lower or proper");
return;
}

// process the contents of the container macro
var openTag = macroName
, closeTag = "end" + macroName
, start = parser.source.indexOf(">>", parser.matchStart) + 2
, end = -1
, tagBegin = start
, tagEnd = start
, opened = 1;
while ((tagBegin = parser.source.indexOf("<<", tagEnd)) !== -1
&amp;&amp; (tagEnd = parser.source.indexOf(">>", tagBegin)) !== -1)
{
var tagName = parser.source.slice(tagBegin + 2, tagEnd)
, tagDelim = tagName.search(/\s/);
if (tagDelim !== -1)
{
tagName = tagName.slice(0, tagDelim);
}
tagEnd += 2;
switch (tagName)
{
case closeTag:
opened--;
break;
case openTag:
opened++;
break;
}
if (opened === 0)
{
end = tagBegin;
break;
}
}

// if we successfully found an end tag for the macro
if (end !== -1)
{
parser.nextMatch = tagEnd;

this.n = params[0];
if (this.n === "upper") //changes the spanned text with uppercase text
{
this.m = parser.source.slice(start, end);
this.f = this.m.toUpperCase();
}
else if (this.n === "lower") //changes the spanned text with lowercase text
{
this.m = parser.source.slice(start, end);
this.f = this.m.toLowerCase();
}
else if (this.n === "proper") //changes the spanned text with proper/titlecase text - note currently this only works for the first word in a sentence.
{
this.m = parser.source.slice(start, end);
this.f = this.m.slice(0,1).toUpperCase() + this.m.slice(1).toLowerCase();
}

new Wikifier(place, this.f);
}
else
{
throwError(place, "<<" + macroName + ">>: cannot find a matching close tag");
}
},
};
macros["endchangecase"] = { handler: function () {} };
Now, the thing is, this doesn't seem to play well with a <<print $variable>> nested between the start and end tags, with Twine returning a Macro not found: Print error if I try to nest it.  The original code this was adapted from seems to have a <<print>> macro nested within the start and end tags, so I presumed that it should work.  So, I guess I'm wondering why it doesn't, because really, this changecase macro would come in handy mainly for use with variables (for instance, if you wish to use title case on a variable because it comes at the end of the sentence, or you have an NPC shouting the characters player defined name).  I know these can be achieved with javascript, but I know it'd come in handy for me to have a case macro like this just so I don't have to remember what the correct piece of javascript is all the time. 

As always, any help or advice is much appreciated!

Comments

  • I wouldn't even use a container macro for this.  I'd suggest something like this instead:

    // PROTOTYPE:
    // <<lowercase EXPRESSION>>
    // <<uppercase EXPRESSION>>
    // <<titlecase EXPRESSION>>
    // n.b. EXPRESSION means you can use it pretty much like you would <<print>>
    // USAGE:
    // <<lowercase "text">> <<lowercase $variableText>> <<lowercase "text" + $variableText + aFunctionThatReturnsText()>>
    // <<uppercase "text">> <<uppercase $variableText>> <<uppercase "text" + $variableText + aFunctionThatReturnsText()>>
    // <<titlecase "text">> <<titlecase $variableText>> <<titlecase "text" + $variableText + aFunctionThatReturnsText()>>

    version.extensions["caseMacros"] = { major: 1, minor: 0, revision: 0 };
    macros["lowercase"] = macros["uppercase"] = macros["titlecase"] =
    {
    handler: function(place, macroName, params, parser)
    {
    if (parser.fullArgs().trim() === "")
    {
    throwError(place, "<<" + macroName + ">>: no text specified");
    return;
    }

    try
    {
    var result = eval(parser.fullArgs());
    switch (macroName)
    {
    case "lowercase":
    result = result.toLowerCase();
    break;
    case "uppercase":
    result = result.toUpperCase();
    break;
    case "titlecase":
    result = result.slice(0, 1).toUpperCase() + result.slice(1).toLowerCase();
    break;
    }

    new Wikifier(place, result);
    }
    catch (e)
    {
    throwError(place, "<<" + macroName + ">>: " + e.message);
    return;
    }
    }
    };
  • Well, that certainly seems to do the trick, thanks.  ;D  I just used the container macro as it was the only example I could find that I could really get my head around!

    Obviously I'm still very new to this, so just trying to study as many examples as custom macros as I can to get an idea of how they can be built/used. 
  • This CSS and HTML might also be sufficient, albeit admittedly more verbose:

    .titlecase { text-transform: capitalize }
    .uppercase { text-transform: uppercase }
    .lowercase { text-transform: lowercase }

    <span class="uppercase"><<$variable>></span>
    <span class="lowercase"><<$variable>></span>
    I guess one difference is that "text-transform: capitalize" capitalises each word and not, as the macro does, just the first...
  • Well I have to confess I would never have thought to use css solution for this, but I can see it works just as well.  It's also useful to know just for the "text-transform: capitalize" bit, I was aware in my first attempt that the macro was only capitalising the first letter of an entire passage, which might be useful, but if it was for something like a full name i.e. John Smith, not so great.  Thanks :)
Sign In or Register to comment.