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.