From 61a4fbe68fa98cd258ef74cd46d167c1e26a6dfc Mon Sep 17 00:00:00 2001 From: ChapelR Date: Sun, 26 Jan 2020 06:20:15 -0500 Subject: [PATCH] v0.2.2 --- .gitignore | 3 +- docs/.nojekyll | 0 README.md => docs/README.md | 0 docs/_navbar.md | 4 + changelog.md => docs/changelog.md | 10 +- docs/demo/index.html | 532 ++++++++++++++++++ docs/index.html | 23 + .../installation-guide.md | 0 examples/articles.js | 195 +++++++ examples/dice.js | 152 +++++ macro.js | 4 +- package.json | 2 +- 12 files changed, 920 insertions(+), 5 deletions(-) create mode 100644 docs/.nojekyll rename README.md => docs/README.md (100%) create mode 100644 docs/_navbar.md rename changelog.md => docs/changelog.md (50%) create mode 100644 docs/demo/index.html create mode 100644 docs/index.html rename installation-guide.md => docs/installation-guide.md (100%) create mode 100644 examples/articles.js create mode 100644 examples/dice.js diff --git a/.gitignore b/.gitignore index ed7b0e5..8bd50d9 100644 --- a/.gitignore +++ b/.gitignore @@ -102,5 +102,6 @@ dist # TernJS port file .tern-port -*.zip +**.zip +**.bat test/ \ No newline at end of file diff --git a/docs/.nojekyll b/docs/.nojekyll new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/docs/README.md similarity index 100% rename from README.md rename to docs/README.md diff --git a/docs/_navbar.md b/docs/_navbar.md new file mode 100644 index 0000000..eaaff3d --- /dev/null +++ b/docs/_navbar.md @@ -0,0 +1,4 @@ +- [Docs](/) +- [Install](installation-guide.md) +- [Changelog](changelog.md) +- [Demo](/demo ':ignore') \ No newline at end of file diff --git a/changelog.md b/docs/changelog.md similarity index 50% rename from changelog.md rename to docs/changelog.md index d437e4c..91f60e7 100644 --- a/changelog.md +++ b/docs/changelog.md @@ -1,9 +1,17 @@ ## Pre-release Versions +### v0.2.2 + +- **[Update]** Internal improvements and bug fixes. +- **[Examples]** Added two more examples: + - `(dice:)` A port of the CMFSC2 macro. + - `(article:)`/`(set-article:)` A port of the CMFSC2 macro. +- **[Meta]** Demo and docs website. + ### v0.2.1 - **[Update]** Fixed bug in `Harlowe.variable()`. -- **[New]** Added two examples: +- **[Examples]** Added two examples: - `(textbox:)` A basic text input implementation. - `(hotkey:)` A basic hotkey / click link with keypress implementation. - **[Docs]** Docs typos. diff --git a/docs/demo/index.html b/docs/demo/index.html new file mode 100644 index 0000000..58dadd6 --- /dev/null +++ b/docs/demo/index.html @@ -0,0 +1,532 @@ + + + + +Custom Macro Testing Suite + + + + + + + + + + + + + + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..a159497 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,23 @@ + + + + + Harlowe Custom Macro API + + + + + + +
+ + + + diff --git a/installation-guide.md b/docs/installation-guide.md similarity index 100% rename from installation-guide.md rename to docs/installation-guide.md diff --git a/examples/articles.js b/examples/articles.js new file mode 100644 index 0000000..8034005 --- /dev/null +++ b/examples/articles.js @@ -0,0 +1,195 @@ +/* + + PORTED FROM CMFSC2 + + (article: text [, capitalize]) + + Returns the text with the correct article ('a' or 'an') appended. + + - text ( string ) A string of text to append an article to. + - capitalize ( boolean ) ( optional ) Whether to capitalize the result. + + Example: + (article: 'UFO', true) + (article: 'European') + (article: 'eleven', true) + (article: 'angel') + (article: 'honor', true) + (article: 'idol') + + + (set-article: article, text [, caseSensitive]) + + Overrides the normal result of the `(article:)` macro with the indicated article when testing the indicated text. + + It is highly recommended that all `(set-article:)` calls go in a `startup`-tagged passage! + + - article ( string ) The article ('a' or 'an') to use. + - text ( string ) The text to override the article of. + - caseSensitive ( boolean ) ( optional ) If included and true, only exact case matches of the text will be overridden. + + Example: + (set-article: 'a', 'US', true) + + (article: 'us')-vs-them situation + (article: 'US') citizen +*/ + +(function () { + 'use strict'; + + var _overrides = new Map(); + + var _defaultIrregulars = [ // lifted from: https://github.com/tandrewnichols/indefinite/blob/master/lib/irregular-words.js + // e + 'eunuch', 'eucalyptus', 'eugenics', 'eulogy', 'euphemism', 'euphony', 'euphoria', 'eureka', 'european', 'euphemistic', 'euphonic', 'euphoric', 'euphemistically', 'euphonically', 'euphorically', + // h + 'heir', 'heiress', 'herb', 'homage', 'honesty', 'honor', 'honour', 'hour', 'honest', 'honorous', 'honestly', 'hourly', + // o + 'one', 'ouija', 'once', + // u + 'ubiquitous', 'ugandan', 'ukrainian', 'unanimous', 'unicameral', 'unified', 'unique', 'unisex', 'universal', 'urinal', 'urological', 'useful', 'useless', 'usurious', 'usurped', 'utilitarian', 'utopic', 'ubiquitously', 'unanimously', 'unicamerally', 'uniquely', 'universally', 'urologically', 'usefully', 'uselessly', 'usuriously' + ]; + + var _validArticles = ['a', 'an']; + + var _vowels = /[aeiou8]/i; + var _acronyms = /[A-Z]+$/; + var _irregularAcronyms = /[UFHLMNRSX]/; + var _punctuation = /[.,\/#!$%\^&\*;:{}=\-_`~()]/g; + + function _switch (article) { + if (article === 'a') { + return 'an'; + } + return 'a'; + } + + function _isAcronym (word, article) { + if (_acronyms.test(word) && _irregularAcronyms.test(word.charAt(0))) { + return _switch(article); + } + return false; + } + + function _isDefaultIrregular (word, article) { + if (_defaultIrregulars.includes(word.toLowerCase())) { + return _switch(article); + } + return false; + } + + function addOverride (article, word, caseSensitive) { + var msg; + // check args + if (!word || typeof word !== 'string') { + msg = 'cannot add article override -> invalid word'; + console.error(msg); + return msg; + } + if (article && typeof article === 'string') { + // clean up article + article = article.toLowerCase().trim(); + } + if (!_validArticles.includes(article)) { + msg = 'cannot add article override -> invalid article, must be "a" or "an"'; + console.error(msg); + return msg; + } + // clean up phrase + word = word.trim(); + + if (caseSensitive) { + _overrides.set(word, { + article : article, + caseSensitive : !!caseSensitive + }); + } else { + _overrides.set(word.toLowerCase(), { + article : article, + caseSensitive : !!caseSensitive + }); + } + } + + function _checkOverrides (word) { + word = word.trim(); + // check user-defined overrides + var check = word.toLowerCase(); + if (_overrides.has(check) || _overrides.has(word)) { + var override = _overrides.get(check) || _overrides.get(word); + // check if we require an exact (case-sensitive) match + if (override.caseSensitive && !_overrides.has(word)) { + return null; + } + // return the article + return override.article; + } + // return nothing, passing on to built-in article checks + return null; + } + + function _checkVowels (word) { + var article; + // select the article based on vowels + if (_vowels.test(word.charAt(0))) { + article = 'an'; + } else { + article = 'a'; + } + // check for irregular words, then acronyms + return _isDefaultIrregular(word, article) || _isAcronym(word, article) || article; + } + + function find (word) { + if (!word || typeof word !== 'string') { + return; + } + var cleanedWord = word.trim().split(' ')[0].trim(); + cleanedWord = cleanedWord.replace(_punctuation, ''); + return _checkOverrides(cleanedWord) || _checkVowels(cleanedWord); + } + + function article (word, upper) { + if (!word || typeof word !== 'string') { + return word; // ? just throw back whatever we got + } + var article = find(word); + // return article, capitalized if requested, appended to the original phrase + return (upper ? article.charAt(0).toUpperCase() + article.substring(1) : article) + ' ' + word; + } + + window.setup = window.setup || {}; + + Object.assign(window.setup, { + find : find, + output : article, + override : addOverride + }); + + Harlowe.macro('article', function (text, uc) { + var err = this.typeCheck([ + 'string', + 'boolean|undefined' + ]); + if (err) throw err; + + return article(text, uc); + }); + + Harlowe.macro('setarticle', function (article, word, caseSen) { + var err = this.typeCheck([ + 'string', + 'string', + 'boolean|undefined' + ]); + if (err) throw err; + + if (!_validArticles.includes(article.toLowerCase().trim())) { + throw this.error('invalid article in first argument--must be "a" or "an"'); + } + + addOverride(article, word, caseSen); + }); + +}()); \ No newline at end of file diff --git a/examples/dice.js b/examples/dice.js new file mode 100644 index 0000000..9ee7033 --- /dev/null +++ b/examples/dice.js @@ -0,0 +1,152 @@ +/* + + PORTED FROM CMFSC2 (somewhat) + + (dice: notation) + (dice: number, sides [, modifier]) + + Rolls dice and returns the result. + + - notation ( string ) A string of valid dice notation, e.g. '1d6', '3d8+3', '2d10 - 2', '3dF' (fate/fudge dice) + - number ( number ) The number of dice to roll. + - sides ( number | string ) The sides or type of dice to roll ('F' is the only accepted string value). + - modifier ( number ) ( optional ) The modifier; a flat number to add to the dice roll. May be negative to represent a subtraction. + + Example: + + (set: _roll to (dice: '3d6+10')) + (set: _roll to (dice: '3d6 + 10')) + (set: _roll to (dice: 3, 6, 10)) + (set: _roll to (dice: 3, 6) + 10) + +*/ + +(function () { + 'use strict'; + + // dice roller + + var PATTERN = /(\d+)d(\d+|f)(\s*([+-])\s*(\d+))?/i + var MATCHES = [ + 'roll', // 0 + 'amount', // 1 + 'type', // 2 + 'mod_whole', // 3 + 'mod_sign', // 4 + 'mod_value' // 5 + ]; + + function parse (notation) { + if (!notation || typeof notation !== 'string') { + return; + } + var notn = notation.trim().toLowerCase(); + if (!notn || !notn.includes('d')) { + return; + } + var m = PATTERN.exec(notn); + if (!m || !(m instanceof Array) || !m.length) { + return; + } + var ret = {}; + MATCHES.forEach( function (prop, idx) { + ret[prop] = m[idx] || ''; + }); + return ret; + } + + function rollDice (num, type, rnd) { + if (!rnd || typeof rnd !== 'function') { + rnd = Math.random; + } + var add = 1; + var result = 0; + var rolls = []; + num = Number(num); + + if (typeof type === 'string' && type.trim().toUpperCase() === 'F') { + type = 3; // -1, 0, or 1 + add = -1; + } else { + type = Number(type); + } + + if ([num, type, add].some( function (n) { + return Number.isNaN(n); + })) { + throw new TypeError('could not process arguments in dice roll'); + } + + for (var i = 0; i < num; i++) { + var die = 0; + var random = rnd(); + if (typeof random !== 'number' || random > 1 || random < 0) { + throw new TypeError('invalid random function--should return a value between 0 and 1'); + } + die = Math.floor(random * type) + add; + rolls.push(die); + result += die; // update result + } + + return { + rolls : rolls, + result : result + }; + } + + function prepNotation (obj) { + var n = Number(obj.amount); + var t = Number(obj.type); + var m = Number(obj.mod_value); + if (obj.mod_sign.trim() === '-') { + m = m * -1; + } + return { + number : n, + type : t, + modifier : m + }; + } + + function makeRoll (num, type, mod, rnd) { + var roll = rollDice(num, type, rnd).result; + if (mod) { + roll += mod; + } + return roll; + } + + function roll (a, b, c) { + var r = {}; + if (typeof a === 'string') { + r = prepNotation(parse(a)); + } else { + r.number = a; + r.type = b; + } + if (!r.modifier && c && typeof c === 'number') { + r.modifier = c || 0; + } else if (!r.modifier) { + r.modifier = 0; + } + return makeRoll(r.number, r.type, r.modifier, null); + } + + Harlowe.macro('dice', function (a, b, c) { + var err = this.typeCheck([ + 'string|number', + 'number|undefined', + 'number|undefined' + ]); + if (err) throw err; + + return roll(a, b, c); + }); + + window.setup = window.setup || {}; + + Object.assign(window.setup, { + dice : roll + }); + +}()); \ No newline at end of file diff --git a/macro.js b/macro.js index d618ee9..03caa72 100644 --- a/macro.js +++ b/macro.js @@ -150,9 +150,9 @@ throw new TypeError('Invalid macro handler.'); } if (changer && typeof changer === 'function') { - simplMacro(name, cb, changer); + simpleChangerMacro(name, cb, changer); } else { - simpleCommandMacro(name, cb); + simpleMacro(name, cb); } } diff --git a/package.json b/package.json index 5aee19a..d5d7862 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "harlowe-macro-api", - "version": "0.2.1", + "version": "0.2.2", "description": "Unofficial Macro API for Twine 2/Harlowe.", "main": "build.js", "dependencies": {},