Created
March 4, 2018 19:36
-
-
Save mildmojo/40db613f21b67fa22802d29f6aeb9969 to your computer and use it in GitHub Desktop.
More logic operators for bitsy. &&, ||, !==, &&!, and ||!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* Operator logic is at the bottom of this script; need to declare the toolkit first. */ | |
/* | |
================================ | |
SCRIPT HOOKS TOOLKIT (@mildmojo) | |
================================ | |
HOW TO USE: | |
1. Paste this whole file in script tags at the bottom of your Bitsy | |
exported game HTML, after the last /script> tag. | |
*/ | |
(function scriptHooksToolkit(globals) { | |
// Allow multiple copies of this script to work in one HTML file. | |
globals.queuedInjectScripts = globals.queuedInjectScripts || []; | |
globals.queuedBeforeScripts = globals.queuedBeforeScripts || {}; | |
globals.queuedAfterScripts = globals.queuedAfterScripts || []; | |
globals.superFuncs = globals.superFuncs || {}; | |
globals.injectsDone = globals.injectsDone || false; | |
var queuedInjectScripts = globals.queuedInjectScripts; | |
var queuedBeforeScripts = globals.queuedBeforeScripts; | |
var queuedAfterScripts = globals.queuedAfterScripts; | |
var superFuncs = globals.superFuncs; | |
var injectsDone = globals.injectsDone; | |
var oldStartFunc = startExportedGame; | |
globals.startExportedGame = function doAllInjections() { | |
// Only do this once. | |
globals.startExportedGame = oldStartFunc; | |
if (injectsDone) { | |
return oldStartFunc(); | |
} | |
globals.injectsDone = true; | |
// Rewrite scripts and hook everything up. | |
doInjects(); | |
hookBefores(); | |
hookAfters(); | |
// Start the game. If original `startExportedGame` wasn't overwritten, call it. | |
if (startExportedGame === oldStartFunc) { | |
oldStartFunc.apply(this, arguments); | |
} else { | |
startExportedGame.apply(this, arguments); | |
} | |
}; | |
// Examples: inject('names.sprite', 'console.dir(names)'); | |
// inject('names.sprite', 'console.dir(names);', 'console.dir(sprite);'); | |
// inject('names.sprite', ['console.dir(names)', 'console.dir(sprite);']); | |
globals.inject = function(searchString, codeFragments) { | |
var args = [].slice.call(arguments); | |
codeFragments = _flatten(args.slice(1)); | |
queuedInjectScripts.push({ | |
searchString: searchString, | |
codeFragments: codeFragments | |
}); | |
}; | |
// Ex: before('load_game', function run() { alert('Loading!'); }); | |
// before('show_text', function run(text) { return text.toUpperCase(); }); | |
// before('show_text', function run(text, done) { done(text.toUpperCase()); }); | |
globals.before = function(targetFuncName, beforeFn) { | |
queuedBeforeScripts[targetFuncName] = queuedBeforeScripts[targetFuncName] || []; | |
queuedBeforeScripts[targetFuncName].push(beforeFn); | |
}; | |
// Ex: after('load_game', function run() { alert('Loaded!'); }); | |
globals.after = function(targetFuncName, afterFn) { | |
queuedAfterScripts.push({ | |
targetFuncName: targetFuncName, | |
runFunc: afterFn | |
}); | |
}; | |
function doInjects() { | |
queuedInjectScripts.forEach(function(injectScript) { | |
_inject(injectScript.searchString, injectScript.codeFragments); | |
}); | |
_reinitEngine(); | |
} | |
function hookBefores() { | |
Object.keys(queuedBeforeScripts).forEach(function(targetFuncName) { | |
var superFn = globals[targetFuncName]; | |
var beforeFuncs = queuedBeforeScripts[targetFuncName]; | |
globals[targetFuncName] = function() { | |
var self = this; | |
var args = [].slice.call(arguments); | |
var i = 0; | |
runBefore.call(self); | |
// Iterate thru sync & async functions. Run each, finally run original. | |
function runBefore() { | |
// Update args if provided. | |
if (arguments.length > 0) { | |
args = [].slice.call(arguments); | |
} | |
// All outta before functions? Finish by running the original. | |
if (i === beforeFuncs.length) { | |
return superFn.apply(self, args); | |
} | |
// Assume before funcs that accept more args than the original are | |
// async and have a spot for an async callback. | |
if (beforeFuncs[i].length > superFn.length) { | |
beforeFuncs[i++].apply(self, args.concat(runBefore)); | |
} else { | |
var newArgs = beforeFuncs[i++].apply(self, args) || args; | |
runBefore.apply(self, newArgs); | |
} | |
} | |
}; | |
}); | |
} | |
function hookAfters() { | |
queuedAfterScripts.forEach(function(afterScript) { | |
_after(afterScript.targetFuncName, afterScript.runFunc); | |
}); | |
} | |
function _inject(searchString, codeToInject) { | |
var args = [].slice.call(arguments); | |
codeToInject = _flatten(args.slice(1)).join("\n"); | |
// find the relevant script tag | |
var scriptTags = document.getElementsByTagName('script'); | |
var scriptTag; | |
var code; | |
for (var i = 0; i < scriptTags.length; ++i) { | |
scriptTag = scriptTags[i]; | |
var matchesSearch = scriptTag.textContent.indexOf(searchString) !== -1; | |
var isCurrentScript = scriptTag === document.currentScript; | |
if (matchesSearch && !isCurrentScript) { | |
code = scriptTag.textContent; | |
break; | |
} | |
} | |
// error-handling | |
if (!code) { | |
throw 'Couldn\'t find "' + searchString + '" in script tags'; | |
} | |
// modify the content | |
code = code.replace(searchString, searchString + codeToInject); | |
// replace the old script tag with a new one using our modified code | |
scriptTag.remove(); | |
scriptTag = document.createElement('script'); | |
scriptTag.textContent = code; | |
document.head.appendChild(scriptTag); | |
} | |
function _after(targetFuncName, afterFn) { | |
var superFn = globals[targetFuncName]; | |
globals[targetFuncName] = function() { | |
superFn.apply(this, arguments); | |
afterFn.apply(this, arguments); | |
}; | |
} | |
function _reinitEngine() { | |
// recreate the script and dialog objects so that they'll be | |
// referencing the code with injections instead of the original | |
globals.scriptModule = new Script(); | |
globals.scriptInterpreter = scriptModule.CreateInterpreter(); | |
globals.dialogModule = new Dialog(); | |
globals.dialogRenderer = dialogModule.CreateRenderer(); | |
globals.dialogBuffer = dialogModule.CreateBuffer(); | |
} | |
function _flatten(list) { | |
if (!Array.isArray(list)) { | |
return list; | |
} | |
return list.reduce(function(fragments, arg) { | |
return fragments.concat(_flatten(arg)); | |
}, []); | |
} | |
})(window); | |
/* | |
===================================== | |
ADD CONDITIONAL OPERATORS (@mildmojo) | |
===================================== | |
Adds conditional logic operators: | |
- !== (not equal to) | |
- && (and) | |
- || (or) | |
- &&! (and not) | |
- ||! (or not) | |
Examples: candlecount > 5 && haslighter == 1 | |
candlecount > 5 && papercount > 1 && isIndoors | |
haslighter == 1 || hasmatches == 1 | |
candlecount > 5 && candlecount !== 666 | |
candlecount > 5 &&! droppedlighter | |
droppedlighter ||! hasmatches | |
NOTE: The combining operators (&&, ||, &&!, ||!) have lower precedence than | |
all other math and comparison operators, so it might be hard to write | |
tests that mix and match these new operators and have them evaluate | |
correctly. If you're using multiple `&&` and `||` operators in one | |
condition, be sure to test every possibility to make sure it behaves | |
the way you want. | |
*/ | |
(function(globals) { | |
'use strict'; | |
inject('operatorMap.set("-", subExp);', | |
'operatorMap.set("!==", notEqExp);', | |
'operatorMap.set("&&", andExp);', | |
'operatorMap.set("||", orExp);', | |
'operatorMap.set("&&!", andNotExp);', | |
'operatorMap.set("||!", orNotExp);'); | |
inject('var operatorSymbols = ["-", "+", "/", "*", "<=", ">=", "<", ">", "=="];', | |
'operatorSymbols.unshift("!==", "&&", "||", "&&!", "||!");'); | |
globals.andExp = function andExp(environment,left,right,onReturn) { | |
right.Eval(environment,function(rVal){ | |
left.Eval(environment,function(lVal){ | |
onReturn( lVal && rVal ); | |
}); | |
}); | |
}; | |
globals.orExp = function orExp(environment,left,right,onReturn) { | |
right.Eval(environment,function(rVal){ | |
left.Eval(environment,function(lVal){ | |
onReturn( lVal || rVal ); | |
}); | |
}); | |
}; | |
globals.notEqExp = function notEqExp(environment,left,right,onReturn) { | |
right.Eval(environment,function(rVal){ | |
left.Eval(environment,function(lVal){ | |
onReturn( lVal !== rVal ); | |
}); | |
}); | |
}; | |
globals.andNotExp = function andNotExp(environment,left,right,onReturn) { | |
right.Eval(environment,function(rVal){ | |
left.Eval(environment,function(lVal){ | |
onReturn( lVal && !rVal ); | |
}); | |
}); | |
}; | |
globals.orNotExp = function orNotExp(environment,left,right,onReturn) { | |
right.Eval(environment,function(rVal){ | |
left.Eval(environment,function(lVal){ | |
onReturn( lVal || !rVal ); | |
}); | |
}); | |
}; | |
})(window); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment