From a7dcb503aa8ccfc908cfcfbe1a895b335588dfae Mon Sep 17 00:00:00 2001 From: walterhiggins Date: Tue, 24 Dec 2013 00:09:49 +0000 Subject: [PATCH] Major overhaul of plugin and module loading system and scriptcraft directory layout --- .../javascript/drone/{ => contrib}/partial.js | 0 .../drone/{ => contrib}/rboxcall.js | 0 src/main/javascript/ext/json2.js | 486 ----- .../javascript/{core => lib}/_primitives.js | 0 src/main/javascript/{events => lib}/events.js | 0 src/main/javascript/lib/plugin.js | 208 ++ .../{core/_require.js => lib/require.js} | 0 .../_scriptcraft.js => lib/scriptcraft.js} | 0 .../{ => modules}/fireworks/fireworks.js | 0 .../javascript/modules/fireworks/package.json | 4 + src/main/javascript/modules/http/package.json | 4 + .../javascript/{ => modules}/http/request.js | 0 src/main/javascript/modules/partial.js | 14 + .../javascript/{ => modules}/signs/menu.js | 0 .../javascript/modules/signs/package.json | 4 + .../modules/underscore/package.json | 4 + .../modules/underscore/underscore.js | 1314 +++++++++++++ .../javascript/modules/utils/package.json | 4 + .../text.js => modules/utils/string-exts.js} | 0 .../javascript/{ => modules}/utils/utils.js | 0 .../javascript/{ => plugins}/alias/alias.js | 0 .../javascript/{arrows => plugins}/arrows.js | 0 .../javascript/{ => plugins}/chat/color.js | 0 .../{ => plugins}/classroom/classroom.js | 0 src/main/javascript/plugins/drone/blocks.js | 275 +++ .../javascript/plugins/drone/blocktype.js | 376 ++++ .../plugins/drone/contrib/castle.js | 51 + .../plugins/drone/contrib/chessboard.js | 37 + .../plugins/drone/contrib/cottage.js | 79 + .../plugins/drone/contrib/dancefloor.js | 38 + .../javascript/plugins/drone/contrib/fort.js | 68 + .../javascript/plugins/drone/contrib/logo.js | 219 +++ .../plugins/drone/contrib/rainbow.js | 44 + .../plugins/drone/contrib/rboxcall.js | 34 + .../plugins/drone/contrib/redstonewire.js | 108 + .../drone/contrib/skyscraper-example.js | 19 + .../plugins/drone/contrib/spiral_stairs.js | 48 + .../plugins/drone/contrib/streamer.js | 32 + .../plugins/drone/contrib/temple.js | 25 + .../javascript/plugins/drone/drone-exts.js | 25 + .../plugins/drone/drone-firework.js | 6 + src/main/javascript/plugins/drone/drone.js | 1740 +++++++++++++++++ src/main/javascript/plugins/drone/sphere.js | 268 +++ src/main/javascript/plugins/drone/test.js | 23 + src/main/javascript/plugins/example-1.js | 12 + .../javascript/{ => plugins}/homes/homes.js | 0 .../{ => plugins}/minigames/NumberGuess.js | 0 .../{ => plugins}/minigames/SnowBallFight.js | 0 src/main/javascript/plugins/signs/examples.js | 32 + 49 files changed, 5115 insertions(+), 486 deletions(-) rename src/main/javascript/drone/{ => contrib}/partial.js (100%) rename src/main/javascript/drone/{ => contrib}/rboxcall.js (100%) delete mode 100644 src/main/javascript/ext/json2.js rename src/main/javascript/{core => lib}/_primitives.js (100%) rename src/main/javascript/{events => lib}/events.js (100%) create mode 100644 src/main/javascript/lib/plugin.js rename src/main/javascript/{core/_require.js => lib/require.js} (100%) rename src/main/javascript/{core/_scriptcraft.js => lib/scriptcraft.js} (100%) rename src/main/javascript/{ => modules}/fireworks/fireworks.js (100%) create mode 100644 src/main/javascript/modules/fireworks/package.json create mode 100644 src/main/javascript/modules/http/package.json rename src/main/javascript/{ => modules}/http/request.js (100%) create mode 100644 src/main/javascript/modules/partial.js rename src/main/javascript/{ => modules}/signs/menu.js (100%) create mode 100644 src/main/javascript/modules/signs/package.json create mode 100644 src/main/javascript/modules/underscore/package.json create mode 100644 src/main/javascript/modules/underscore/underscore.js create mode 100644 src/main/javascript/modules/utils/package.json rename src/main/javascript/{utils/text.js => modules/utils/string-exts.js} (100%) rename src/main/javascript/{ => modules}/utils/utils.js (100%) rename src/main/javascript/{ => plugins}/alias/alias.js (100%) rename src/main/javascript/{arrows => plugins}/arrows.js (100%) rename src/main/javascript/{ => plugins}/chat/color.js (100%) rename src/main/javascript/{ => plugins}/classroom/classroom.js (100%) create mode 100644 src/main/javascript/plugins/drone/blocks.js create mode 100644 src/main/javascript/plugins/drone/blocktype.js create mode 100644 src/main/javascript/plugins/drone/contrib/castle.js create mode 100644 src/main/javascript/plugins/drone/contrib/chessboard.js create mode 100644 src/main/javascript/plugins/drone/contrib/cottage.js create mode 100644 src/main/javascript/plugins/drone/contrib/dancefloor.js create mode 100644 src/main/javascript/plugins/drone/contrib/fort.js create mode 100644 src/main/javascript/plugins/drone/contrib/logo.js create mode 100644 src/main/javascript/plugins/drone/contrib/rainbow.js create mode 100644 src/main/javascript/plugins/drone/contrib/rboxcall.js create mode 100644 src/main/javascript/plugins/drone/contrib/redstonewire.js create mode 100644 src/main/javascript/plugins/drone/contrib/skyscraper-example.js create mode 100644 src/main/javascript/plugins/drone/contrib/spiral_stairs.js create mode 100644 src/main/javascript/plugins/drone/contrib/streamer.js create mode 100644 src/main/javascript/plugins/drone/contrib/temple.js create mode 100644 src/main/javascript/plugins/drone/drone-exts.js create mode 100644 src/main/javascript/plugins/drone/drone-firework.js create mode 100644 src/main/javascript/plugins/drone/drone.js create mode 100644 src/main/javascript/plugins/drone/sphere.js create mode 100644 src/main/javascript/plugins/drone/test.js create mode 100644 src/main/javascript/plugins/example-1.js rename src/main/javascript/{ => plugins}/homes/homes.js (100%) rename src/main/javascript/{ => plugins}/minigames/NumberGuess.js (100%) rename src/main/javascript/{ => plugins}/minigames/SnowBallFight.js (100%) create mode 100644 src/main/javascript/plugins/signs/examples.js diff --git a/src/main/javascript/drone/partial.js b/src/main/javascript/drone/contrib/partial.js similarity index 100% rename from src/main/javascript/drone/partial.js rename to src/main/javascript/drone/contrib/partial.js diff --git a/src/main/javascript/drone/rboxcall.js b/src/main/javascript/drone/contrib/rboxcall.js similarity index 100% rename from src/main/javascript/drone/rboxcall.js rename to src/main/javascript/drone/contrib/rboxcall.js diff --git a/src/main/javascript/ext/json2.js b/src/main/javascript/ext/json2.js deleted file mode 100644 index c7745df..0000000 --- a/src/main/javascript/ext/json2.js +++ /dev/null @@ -1,486 +0,0 @@ -/* - json2.js - 2012-10-08 - - Public Domain. - - NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. - - See http://www.JSON.org/js.html - - - This code should be minified before deployment. - See http://javascript.crockford.com/jsmin.html - - USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO - NOT CONTROL. - - - This file creates a global JSON object containing two methods: stringify - and parse. - - JSON.stringify(value, replacer, space) - value any JavaScript value, usually an object or array. - - replacer an optional parameter that determines how object - values are stringified for objects. It can be a - function or an array of strings. - - space an optional parameter that specifies the indentation - of nested structures. If it is omitted, the text will - be packed without extra whitespace. If it is a number, - it will specify the number of spaces to indent at each - level. If it is a string (such as '\t' or ' '), - it contains the characters used to indent at each level. - - This method produces a JSON text from a JavaScript value. - - When an object value is found, if the object contains a toJSON - method, its toJSON method will be called and the result will be - stringified. A toJSON method does not serialize: it returns the - value represented by the name/value pair that should be serialized, - or undefined if nothing should be serialized. The toJSON method - will be passed the key associated with the value, and this will be - bound to the value - - For example, this would serialize Dates as ISO strings. - - Date.prototype.toJSON = function (key) { - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - return this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z'; - }; - - You can provide an optional replacer method. It will be passed the - key and value of each member, with this bound to the containing - object. The value that is returned from your method will be - serialized. If your method returns undefined, then the member will - be excluded from the serialization. - - If the replacer parameter is an array of strings, then it will be - used to select the members to be serialized. It filters the results - such that only members with keys listed in the replacer array are - stringified. - - Values that do not have JSON representations, such as undefined or - functions, will not be serialized. Such values in objects will be - dropped; in arrays they will be replaced with null. You can use - a replacer function to replace those with JSON values. - JSON.stringify(undefined) returns undefined. - - The optional space parameter produces a stringification of the - value that is filled with line breaks and indentation to make it - easier to read. - - If the space parameter is a non-empty string, then that string will - be used for indentation. If the space parameter is a number, then - the indentation will be that many spaces. - - Example: - - text = JSON.stringify(['e', {pluribus: 'unum'}]); - // text is '["e",{"pluribus":"unum"}]' - - - text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); - // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' - - text = JSON.stringify([new Date()], function (key, value) { - return this[key] instanceof Date ? - 'Date(' + this[key] + ')' : value; - }); - // text is '["Date(---current time---)"]' - - - JSON.parse(text, reviver) - This method parses a JSON text to produce an object or array. - It can throw a SyntaxError exception. - - The optional reviver parameter is a function that can filter and - transform the results. It receives each of the keys and values, - and its return value is used instead of the original value. - If it returns what it received, then the structure is not modified. - If it returns undefined then the member is deleted. - - Example: - - // Parse the text. Values that look like ISO date strings will - // be converted to Date objects. - - myData = JSON.parse(text, function (key, value) { - var a; - if (typeof value === 'string') { - a = -/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); - if (a) { - return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], - +a[5], +a[6])); - } - } - return value; - }); - - myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { - var d; - if (typeof value === 'string' && - value.slice(0, 5) === 'Date(' && - value.slice(-1) === ')') { - d = new Date(value.slice(5, -1)); - if (d) { - return d; - } - } - return value; - }); - - - This is a reference implementation. You are free to copy, modify, or - redistribute. -*/ - -/*jslint evil: true, regexp: true */ - -/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply, - call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours, - getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join, - lastIndex, length, parse, prototype, push, replace, slice, stringify, - test, toJSON, toString, valueOf -*/ - - -// Create a JSON object only if one does not already exist. We create the -// methods in a closure to avoid creating global variables. - -if (typeof JSON !== 'object') { - JSON = {}; -} - -(function () { - 'use strict'; - - function f(n) { - // Format integers to have at least two digits. - return n < 10 ? '0' + n : n; - } - - if (typeof Date.prototype.toJSON !== 'function') { - - Date.prototype.toJSON = function (key) { - - return isFinite(this.valueOf()) - ? this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z' - : null; - }; - - String.prototype.toJSON = - Number.prototype.toJSON = - Boolean.prototype.toJSON = function (key) { - return this.valueOf(); - }; - } - - var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - gap, - indent, - meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"' : '\\"', - '\\': '\\\\' - }, - rep; - - - function quote(string) { - -// If the string contains no control characters, no quote characters, and no -// backslash characters, then we can safely slap some quotes around it. -// Otherwise we must also replace the offending characters with safe escape -// sequences. - - escapable.lastIndex = 0; - return escapable.test(string) ? '"' + string.replace(escapable, function (a) { - var c = meta[a]; - return typeof c === 'string' - ? c - : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' : '"' + string + '"'; - } - - - function str(key, holder) { - -// Produce a string from holder[key]. - - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; - -// If the value has a toJSON method, call it to obtain a replacement value. - - if (value && typeof value === 'object' && - typeof value.toJSON === 'function') { - value = value.toJSON(key); - } - -// If we were called with a replacer function, then call the replacer to -// obtain a replacement value. - - if (typeof rep === 'function') { - value = rep.call(holder, key, value); - } - -// What happens next depends on the value's type. - - switch (typeof value) { - case 'string': - return quote(value); - - case 'number': - -// JSON numbers must be finite. Encode non-finite numbers as null. - - return isFinite(value) ? String(value) : 'null'; - - case 'boolean': - case 'null': - -// If the value is a boolean or null, convert it to a string. Note: -// typeof null does not produce 'null'. The case is included here in -// the remote chance that this gets fixed someday. - - return String(value); - -// If the type is 'object', we might be dealing with an object or an array or -// null. - - case 'object': - -// Due to a specification blunder in ECMAScript, typeof null is 'object', -// so watch out for that case. - - if (!value) { - return 'null'; - } - -// Make an array to hold the partial results of stringifying this object value. - - gap += indent; - partial = []; - -// Is the value an array? - - if (Object.prototype.toString.apply(value) === '[object Array]') { - -// The value is an array. Stringify every element. Use null as a placeholder -// for non-JSON values. - - length = value.length; - for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; - } - -// Join all of the elements together, separated with commas, and wrap them in -// brackets. - - v = partial.length === 0 - ? '[]' - : gap - ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' - : '[' + partial.join(',') + ']'; - gap = mind; - return v; - } - -// If the replacer is an array, use it to select the members to be stringified. - - if (rep && typeof rep === 'object') { - length = rep.length; - for (i = 0; i < length; i += 1) { - if (typeof rep[i] === 'string') { - k = rep[i]; - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } else { - -// Otherwise, iterate through all of the keys in the object. - - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = str(k, value); - if (v) { - partial.push(quote(k) + (gap ? ': ' : ':') + v); - } - } - } - } - -// Join all of the member texts together, separated with commas, -// and wrap them in braces. - - v = partial.length === 0 - ? '{}' - : gap - ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' - : '{' + partial.join(',') + '}'; - gap = mind; - return v; - } - } - -// If the JSON object does not yet have a stringify method, give it one. - - if (typeof JSON.stringify !== 'function') { - JSON.stringify = function (value, replacer, space) { - -// The stringify method takes a value and an optional replacer, and an optional -// space parameter, and returns a JSON text. The replacer can be a function -// that can replace values, or an array of strings that will select the keys. -// A default replacer method can be provided. Use of the space parameter can -// produce text that is more easily readable. - - var i; - gap = ''; - indent = ''; - -// If the space parameter is a number, make an indent string containing that -// many spaces. - - if (typeof space === 'number') { - for (i = 0; i < space; i += 1) { - indent += ' '; - } - -// If the space parameter is a string, it will be used as the indent string. - - } else if (typeof space === 'string') { - indent = space; - } - -// If there is a replacer, it must be a function or an array. -// Otherwise, throw an error. - - rep = replacer; - if (replacer && typeof replacer !== 'function' && - (typeof replacer !== 'object' || - typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); - } - -// Make a fake root object containing our value under the key of ''. -// Return the result of stringifying the value. - - return str('', {'': value}); - }; - } - - -// If the JSON object does not yet have a parse method, give it one. - - if (typeof JSON.parse !== 'function') { - JSON.parse = function (text, reviver) { - -// The parse method takes a text and an optional reviver function, and returns -// a JavaScript value if the text is a valid JSON text. - - var j; - - function walk(holder, key) { - -// The walk method is used to recursively walk the resulting structure so -// that modifications can be made. - - var k, v, value = holder[key]; - if (value && typeof value === 'object') { - for (k in value) { - if (Object.prototype.hasOwnProperty.call(value, k)) { - v = walk(value, k); - if (v !== undefined) { - value[k] = v; - } else { - delete value[k]; - } - } - } - } - return reviver.call(holder, key, value); - } - - -// Parsing happens in four stages. In the first stage, we replace certain -// Unicode characters with escape sequences. JavaScript handles many characters -// incorrectly, either silently deleting them, or treating them as line endings. - - text = String(text); - cx.lastIndex = 0; - if (cx.test(text)) { - text = text.replace(cx, function (a) { - return '\\u' + - ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - -// In the second stage, we run the text against regular expressions that look -// for non-JSON patterns. We are especially concerned with '()' and 'new' -// because they can cause invocation, and '=' because it can cause mutation. -// But just to be safe, we want to reject all unexpected forms. - -// We split the second stage into 4 regexp operations in order to work around -// crippling inefficiencies in IE's and Safari's regexp engines. First we -// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we -// replace all simple value tokens with ']' characters. Third, we delete all -// open brackets that follow a colon or comma or that begin the text. Finally, -// we look to see that the remaining characters are only whitespace or ']' or -// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. - - if (/^[\],:{}\s]*$/ - .test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@') - .replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']') - .replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { - -// In the third stage we use the eval function to compile the text into a -// JavaScript structure. The '{' operator is subject to a syntactic ambiguity -// in JavaScript: it can begin a block or an object literal. We wrap the text -// in parens to eliminate the ambiguity. - - j = eval('(' + text + ')'); - -// In the optional fourth stage, we recursively walk the new structure, passing -// each name/value pair to a reviver function for possible transformation. - - return typeof reviver === 'function' - ? walk({'': j}, '') - : j; - } - -// If the text is not JSON parseable, then a SyntaxError is thrown. - - throw new SyntaxError('JSON.parse'); - }; - } -}()); diff --git a/src/main/javascript/core/_primitives.js b/src/main/javascript/lib/_primitives.js similarity index 100% rename from src/main/javascript/core/_primitives.js rename to src/main/javascript/lib/_primitives.js diff --git a/src/main/javascript/events/events.js b/src/main/javascript/lib/events.js similarity index 100% rename from src/main/javascript/events/events.js rename to src/main/javascript/lib/events.js diff --git a/src/main/javascript/lib/plugin.js b/src/main/javascript/lib/plugin.js new file mode 100644 index 0000000..1cea02d --- /dev/null +++ b/src/main/javascript/lib/plugin.js @@ -0,0 +1,208 @@ +var File = java.io.File; +var FileWriter = java.io.FileWriter; +var PrintWriter = java.io.PrintWriter; + +/* + Save a javascript object to a file (saves using JSON notation) +*/ +var _save = function(object, filename){ + var objectToStr = null; + try{ + objectToStr = JSON.stringify(object); + }catch(e){ + print("ERROR: " + e.getMessage() + " while saving " + filename); + return; + } + var f = (filename instanceof File) ? filename : new File(filename); + var out = new PrintWriter(new FileWriter(f)); + out.println( objectToStr ); + out.close(); +}; +/* + plugin management +*/ +var _plugins = {}; + +var _plugin = function(/* String */ moduleName, /* Object */ moduleObject, isPersistent) +{ + // + // don't load plugin more than once + // + if (typeof _plugins[moduleName] != "undefined") + return _plugins[moduleName].module; + + var pluginData = {persistent: isPersistent, module: moduleObject}; + moduleObject.store = moduleObject.store || {}; + _plugins[moduleName] = pluginData; + + if (isPersistent){ + var loadedStore = load(dataDir.canonicalPath + "/" + moduleName + "-store.json"); + if (loadedStore){ + moduleObject.store = loadedStore; + }else{ + if (!moduleObject.store){ + moduleObject.store = {}; + } + } + } + return moduleObject; +}; +/* + allow for deferred execution (once all modules have loaded) +*/ +var _deferred = []; +var _ready = function( func ){ + _deferred.push(func); +}; +var _cmdInterceptors = []; +/* + command management - allow for non-ops to execute approved javascript code. +*/ +var _commands = {}; +exports.commands = _commands; +var _command = function(name,func,options,intercepts) +{ + if (typeof name == "undefined"){ + // it's an invocation from the Java Plugin! + if (__cmdArgs.length === 0) + throw new Error("Usage: jsp command-name command-parameters"); + var name = __cmdArgs[0]; + var cmd = _commands[name]; + if (typeof cmd === "undefined"){ + // it's not a global command - pass it on to interceptors + var intercepted = false; + for (var i = 0;i < _cmdInterceptors.length;i++){ + if (_cmdInterceptors[i](__cmdArgs)) + intercepted = true; + } + if (!intercepted) + self.sendMessage("Command '" + name + "' is not recognised"); + }else{ + func = cmd.callback; + var params = []; + for (var i =1; i < __cmdArgs.length;i++){ + params.push("" + __cmdArgs[i]); + } + return func(params); + } + }else{ + if (typeof options == "undefined") + options = []; + _commands[name] = {callback: func, options: options}; + if (intercepts) + _cmdInterceptors.push(func); + return func; + } +}; + +exports.plugin = _plugin; +exports.command = _command; +exports.save = _save; + +var scriptCraftDir = null; +var pluginDir = null; +var dataDir = null; + +exports.autoload = function(dir) { + + scriptCraftDir = dir; + pluginDir = new File(dir, "plugins"); + dataDir = new File(dir, "data"); + + var _canonize = function(file){ + return "" + file.getCanonicalPath().replaceAll("\\\\","/"); + }; + /* + recursively walk the given directory and return a list of all .js files + */ + var _listSourceFiles = function(store,dir) + { + var files = dir.listFiles(); + for (var i = 0;i < files.length; i++) { + var file = files[i]; + if (file.isDirectory()){ + _listSourceFiles(store,file); + }else{ + if ((file.getCanonicalPath().endsWith(".js") + || file.getCanonicalPath().endsWith(".coffee")) + ) { + store.push(file); + } + } + } + }; + /* + sort so that .js files with same name as parent directory appear before + other files in the same directory + */ + var sortByModule = function(a,b){ + a = _canonize(a); + b = _canonize(b); + var aparts = (""+a).split(/\//); + var bparts = (""+b).split(/\//); + //var adir = aparts[aparts.length-2]; + var adir = aparts.slice(0,aparts.length-1).join("/"); + var afile = aparts[aparts.length-1]; + //var bdir = bparts[bparts.length-2]; + var bdir = bparts.slice(0,bparts.length-1).join("/"); + var bfile = bparts[bparts.length-1]; + + if(adirbdir) return 1; + + afile = afile.match(/[a-zA-Z0-9\-_]+/)[0]; + + if (adir.match(new RegExp(afile + "$"))) + return -1; + else + return 1; + }; + /* + Reload all of the .js files in the given directory + */ + var _reload = function(pluginDir) + { + _loaded = []; + var sourceFiles = []; + _listSourceFiles(sourceFiles,pluginDir); + + //sourceFiles.sort(sortByModule); + + // + // script files whose name begins with _ (underscore) + // will not be loaded automatically at startup. + // These files are assumed to be dependencies/private to plugins + // + // E.g. If you have a plugin called myMiniGame.js in the myMiniGame directory + // and which in addition to myMiniGame.js also includes _myMiniGame_currency.js _myMiniGame_events.js etc. + // then it's assumed that _myMiniGame_currency.js and _myMiniGame_events.js will be loaded + // as dependencies by myMiniGame.js and do not need to be loaded via js reload + // + var len = sourceFiles.length; + if (config.verbose) + logger.info(len + " scriptcraft plugins found."); + for (var i = 0;i < len; i++){ + var pluginPath = _canonize(sourceFiles[i]); + if (config.verbose) + logger.info("Loading plugin: " + pluginPath); + var module = require(pluginPath); + for (var property in module){ + /* + all exports in plugins become global + */ + global[property] = module[property]; + } + } + }; + _reload(pluginDir); +}; +addUnloadHandler(function(){ + // + // save all plugins which have persistent data + // + for (var moduleName in _plugins){ + var pluginData = _plugins[moduleName]; + if (pluginData.persistent) + _save(pluginData.module.store, dataDir.canonicalPath + "/" + moduleName + "-store.json"); + } +}); diff --git a/src/main/javascript/core/_require.js b/src/main/javascript/lib/require.js similarity index 100% rename from src/main/javascript/core/_require.js rename to src/main/javascript/lib/require.js diff --git a/src/main/javascript/core/_scriptcraft.js b/src/main/javascript/lib/scriptcraft.js similarity index 100% rename from src/main/javascript/core/_scriptcraft.js rename to src/main/javascript/lib/scriptcraft.js diff --git a/src/main/javascript/fireworks/fireworks.js b/src/main/javascript/modules/fireworks/fireworks.js similarity index 100% rename from src/main/javascript/fireworks/fireworks.js rename to src/main/javascript/modules/fireworks/fireworks.js diff --git a/src/main/javascript/modules/fireworks/package.json b/src/main/javascript/modules/fireworks/package.json new file mode 100644 index 0000000..99595d8 --- /dev/null +++ b/src/main/javascript/modules/fireworks/package.json @@ -0,0 +1,4 @@ +{ + name: 'fireworks', + main: './fireworks.js' +} diff --git a/src/main/javascript/modules/http/package.json b/src/main/javascript/modules/http/package.json new file mode 100644 index 0000000..a143df2 --- /dev/null +++ b/src/main/javascript/modules/http/package.json @@ -0,0 +1,4 @@ +{ + "name": "http", + "main": "./request.js" +} diff --git a/src/main/javascript/http/request.js b/src/main/javascript/modules/http/request.js similarity index 100% rename from src/main/javascript/http/request.js rename to src/main/javascript/modules/http/request.js diff --git a/src/main/javascript/modules/partial.js b/src/main/javascript/modules/partial.js new file mode 100644 index 0000000..f9c40f7 --- /dev/null +++ b/src/main/javascript/modules/partial.js @@ -0,0 +1,14 @@ +/** +* Create a partial function +* +* Parameters: +* func - base function +* [remaining arguments] - arguments bound to the partial function +*/ +module.exports = function (func /*, 0..n args */) { + var args = Array.prototype.slice.call(arguments, 1); + return function() { + var allArguments = args.concat(Array.prototype.slice.call(arguments)); + return func.apply(this, allArguments); + }; +} diff --git a/src/main/javascript/signs/menu.js b/src/main/javascript/modules/signs/menu.js similarity index 100% rename from src/main/javascript/signs/menu.js rename to src/main/javascript/modules/signs/menu.js diff --git a/src/main/javascript/modules/signs/package.json b/src/main/javascript/modules/signs/package.json new file mode 100644 index 0000000..e59258e --- /dev/null +++ b/src/main/javascript/modules/signs/package.json @@ -0,0 +1,4 @@ +{ + name: 'signs', + main: './menu.js' +} diff --git a/src/main/javascript/modules/underscore/package.json b/src/main/javascript/modules/underscore/package.json new file mode 100644 index 0000000..8c4e2b2 --- /dev/null +++ b/src/main/javascript/modules/underscore/package.json @@ -0,0 +1,4 @@ +{ + name: 'underscore', + main: './underscore.js' +} diff --git a/src/main/javascript/modules/underscore/underscore.js b/src/main/javascript/modules/underscore/underscore.js new file mode 100644 index 0000000..6440a36 --- /dev/null +++ b/src/main/javascript/modules/underscore/underscore.js @@ -0,0 +1,1314 @@ +// Underscore.js 1.5.2 +// http://underscorejs.org +// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors +// Underscore may be freely distributed under the MIT license. + +(function() { + + // Baseline setup + // -------------- + + // Establish the root object, `window` in the browser, or `exports` on the server. + var root = this; + + // Save the previous value of the `_` variable. + var previousUnderscore = root._; + + // Establish the object that gets returned to break out of a loop iteration. + var breaker = {}; + + // Save bytes in the minified (but not gzipped) version: + var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype; + + //use the faster Date.now if available. + var getTime = (Date.now || function() { + return new Date().getTime(); + }); + + // Create quick reference variables for speed access to core prototypes. + var + push = ArrayProto.push, + slice = ArrayProto.slice, + concat = ArrayProto.concat, + toString = ObjProto.toString, + hasOwnProperty = ObjProto.hasOwnProperty; + + // All **ECMAScript 5** native function implementations that we hope to use + // are declared here. + var + nativeForEach = ArrayProto.forEach, + nativeMap = ArrayProto.map, + nativeReduce = ArrayProto.reduce, + nativeReduceRight = ArrayProto.reduceRight, + nativeFilter = ArrayProto.filter, + nativeEvery = ArrayProto.every, + nativeSome = ArrayProto.some, + nativeIndexOf = ArrayProto.indexOf, + nativeLastIndexOf = ArrayProto.lastIndexOf, + nativeIsArray = Array.isArray, + nativeKeys = Object.keys, + nativeBind = FuncProto.bind; + + // Create a safe reference to the Underscore object for use below. + var _ = function(obj) { + if (obj instanceof _) return obj; + if (!(this instanceof _)) return new _(obj); + this._wrapped = obj; + }; + + // Export the Underscore object for **Node.js**, with + // backwards-compatibility for the old `require()` API. If we're in + // the browser, add `_` as a global object via a string identifier, + // for Closure Compiler "advanced" mode. + if (typeof exports !== 'undefined') { + if (typeof module !== 'undefined' && module.exports) { + exports = module.exports = _; + } + exports._ = _; + } else { + root._ = _; + } + + // Current version. + _.VERSION = '1.5.2'; + + // Collection Functions + // -------------------- + + // The cornerstone, an `each` implementation, aka `forEach`. + // Handles objects with the built-in `forEach`, arrays, and raw objects. + // Delegates to **ECMAScript 5**'s native `forEach` if available. + var each = _.each = _.forEach = function(obj, iterator, context) { + if (obj == null) return; + if (nativeForEach && obj.forEach === nativeForEach) { + obj.forEach(iterator, context); + } else if (obj.length === +obj.length) { + for (var i = 0, length = obj.length; i < length; i++) { + if (iterator.call(context, obj[i], i, obj) === breaker) return; + } + } else { + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + if (iterator.call(context, obj[keys[i]], keys[i], obj) === breaker) return; + } + } + }; + + // Return the results of applying the iterator to each element. + // Delegates to **ECMAScript 5**'s native `map` if available. + _.map = _.collect = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context); + each(obj, function(value, index, list) { + results.push(iterator.call(context, value, index, list)); + }); + return results; + }; + + var reduceError = 'Reduce of empty array with no initial value'; + + // **Reduce** builds up a single result from a list of values, aka `inject`, + // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available. + _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduce && obj.reduce === nativeReduce) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator); + } + each(obj, function(value, index, list) { + if (!initial) { + memo = value; + initial = true; + } else { + memo = iterator.call(context, memo, value, index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // The right-associative version of reduce, also known as `foldr`. + // Delegates to **ECMAScript 5**'s native `reduceRight` if available. + _.reduceRight = _.foldr = function(obj, iterator, memo, context) { + var initial = arguments.length > 2; + if (obj == null) obj = []; + if (nativeReduceRight && obj.reduceRight === nativeReduceRight) { + if (context) iterator = _.bind(iterator, context); + return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator); + } + var length = obj.length; + if (length !== +length) { + var keys = _.keys(obj); + length = keys.length; + } + each(obj, function(value, index, list) { + index = keys ? keys[--length] : --length; + if (!initial) { + memo = obj[index]; + initial = true; + } else { + memo = iterator.call(context, memo, obj[index], index, list); + } + }); + if (!initial) throw new TypeError(reduceError); + return memo; + }; + + // Return the first value which passes a truth test. Aliased as `detect`. + _.find = _.detect = function(obj, iterator, context) { + var result; + any(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) { + result = value; + return true; + } + }); + return result; + }; + + // Return all the elements that pass a truth test. + // Delegates to **ECMAScript 5**'s native `filter` if available. + // Aliased as `select`. + _.filter = _.select = function(obj, iterator, context) { + var results = []; + if (obj == null) return results; + if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context); + each(obj, function(value, index, list) { + if (iterator.call(context, value, index, list)) results.push(value); + }); + return results; + }; + + // Return all the elements for which a truth test fails. + _.reject = function(obj, iterator, context) { + return _.filter(obj, function(value, index, list) { + return !iterator.call(context, value, index, list); + }, context); + }; + + // Determine whether all of the elements match a truth test. + // Delegates to **ECMAScript 5**'s native `every` if available. + // Aliased as `all`. + _.every = _.all = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = true; + if (obj == null) return result; + if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context); + each(obj, function(value, index, list) { + if (!(result = result && iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if at least one element in the object matches a truth test. + // Delegates to **ECMAScript 5**'s native `some` if available. + // Aliased as `any`. + var any = _.some = _.any = function(obj, iterator, context) { + iterator || (iterator = _.identity); + var result = false; + if (obj == null) return result; + if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context); + each(obj, function(value, index, list) { + if (result || (result = iterator.call(context, value, index, list))) return breaker; + }); + return !!result; + }; + + // Determine if the array or object contains a given value (using `===`). + // Aliased as `include`. + _.contains = _.include = function(obj, target) { + if (obj == null) return false; + if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1; + return any(obj, function(value) { + return value === target; + }); + }; + + // Invoke a method (with arguments) on every item in a collection. + _.invoke = function(obj, method) { + var args = slice.call(arguments, 2); + var isFunc = _.isFunction(method); + return _.map(obj, function(value) { + return (isFunc ? method : value[method]).apply(value, args); + }); + }; + + // Convenience version of a common use case of `map`: fetching a property. + _.pluck = function(obj, key) { + return _.map(obj, _.property(key)); + }; + + // Convenience version of a common use case of `filter`: selecting only objects + // containing specific `key:value` pairs. + _.where = function(obj, attrs, first) { + if (_.isEmpty(attrs)) return first ? void 0 : []; + return _[first ? 'find' : 'filter'](obj, function(value) { + for (var key in attrs) { + if (attrs[key] !== value[key]) return false; + } + return true; + }); + }; + + // Convenience version of a common use case of `find`: getting the first object + // containing specific `key:value` pairs. + _.findWhere = function(obj, attrs) { + return _.where(obj, attrs, true); + }; + + // Return the maximum element or (element-based computation). + // Can't optimize arrays of integers longer than 65,535 elements. + // See [WebKit Bug 80797](https://bugs.webkit.org/show_bug.cgi?id=80797) + _.max = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.max.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return -Infinity; + var result = {computed : -Infinity, value: -Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed > result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Return the minimum element (or element-based computation). + _.min = function(obj, iterator, context) { + if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) { + return Math.min.apply(Math, obj); + } + if (!iterator && _.isEmpty(obj)) return Infinity; + var result = {computed : Infinity, value: Infinity}; + each(obj, function(value, index, list) { + var computed = iterator ? iterator.call(context, value, index, list) : value; + computed < result.computed && (result = {value : value, computed : computed}); + }); + return result.value; + }; + + // Shuffle an array, using the modern version of the + // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle). + _.shuffle = function(obj) { + var rand; + var index = 0; + var shuffled = []; + each(obj, function(value) { + rand = _.random(index++); + shuffled[index - 1] = shuffled[rand]; + shuffled[rand] = value; + }); + return shuffled; + }; + + // Sample **n** random values from a collection. + // If **n** is not specified, returns a single random element. + // The internal `guard` argument allows it to work with `map`. + _.sample = function(obj, n, guard) { + if (n == null || guard) { + if (obj.length !== +obj.length) obj = _.values(obj); + return obj[_.random(obj.length - 1)]; + } + return _.shuffle(obj).slice(0, Math.max(0, n)); + }; + + // An internal function to generate lookup iterators. + var lookupIterator = function(value) { + if (value == null) return _.identity; + if (_.isFunction(value)) return value; + return _.property(value); + }; + + // Sort the object's values by a criterion produced by an iterator. + _.sortBy = function(obj, iterator, context) { + iterator = lookupIterator(iterator); + return _.pluck(_.map(obj, function(value, index, list) { + return { + value: value, + index: index, + criteria: iterator.call(context, value, index, list) + }; + }).sort(function(left, right) { + var a = left.criteria; + var b = right.criteria; + if (a !== b) { + if (a > b || a === void 0) return 1; + if (a < b || b === void 0) return -1; + } + return left.index - right.index; + }), 'value'); + }; + + // An internal function used for aggregate "group by" operations. + var group = function(behavior) { + return function(obj, iterator, context) { + var result = {}; + iterator = lookupIterator(iterator); + each(obj, function(value, index) { + var key = iterator.call(context, value, index, obj); + behavior(result, key, value); + }); + return result; + }; + }; + + // Groups the object's values by a criterion. Pass either a string attribute + // to group by, or a function that returns the criterion. + _.groupBy = group(function(result, key, value) { + (_.has(result, key) ? result[key] : (result[key] = [])).push(value); + }); + + // Indexes the object's values by a criterion, similar to `groupBy`, but for + // when you know that your index values will be unique. + _.indexBy = group(function(result, key, value) { + result[key] = value; + }); + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = group(function(result, key) { + _.has(result, key) ? result[key]++ : result[key] = 1; + }); + + // Use a comparator function to figure out the smallest index at which + // an object should be inserted so as to maintain order. Uses binary search. + _.sortedIndex = function(array, obj, iterator, context) { + iterator = lookupIterator(iterator); + var value = iterator.call(context, obj); + var low = 0, high = array.length; + while (low < high) { + var mid = (low + high) >>> 1; + iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid; + } + return low; + }; + + // Safely create a real, live array from anything iterable. + _.toArray = function(obj) { + if (!obj) return []; + if (_.isArray(obj)) return slice.call(obj); + if (obj.length === +obj.length) return _.map(obj, _.identity); + return _.values(obj); + }; + + // Return the number of elements in an object. + _.size = function(obj) { + if (obj == null) return 0; + return (obj.length === +obj.length) ? obj.length : _.keys(obj).length; + }; + + // Array Functions + // --------------- + + // Get the first element of an array. Passing **n** will return the first N + // values in the array. Aliased as `head` and `take`. The **guard** check + // allows it to work with `_.map`. + _.first = _.head = _.take = function(array, n, guard) { + if (array == null) return void 0; + if ((n == null) || guard) return array[0]; + if (n < 0) return []; + return slice.call(array, 0, n); + }; + + // Returns everything but the last entry of the array. Especially useful on + // the arguments object. Passing **n** will return all the values in + // the array, excluding the last N. The **guard** check allows it to work with + // `_.map`. + _.initial = function(array, n, guard) { + return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n)); + }; + + // Get the last element of an array. Passing **n** will return the last N + // values in the array. The **guard** check allows it to work with `_.map`. + _.last = function(array, n, guard) { + if (array == null) return void 0; + if ((n == null) || guard) return array[array.length - 1]; + return slice.call(array, Math.max(array.length - n, 0)); + }; + + // Returns everything but the first entry of the array. Aliased as `tail` and `drop`. + // Especially useful on the arguments object. Passing an **n** will return + // the rest N values in the array. The **guard** + // check allows it to work with `_.map`. + _.rest = _.tail = _.drop = function(array, n, guard) { + return slice.call(array, (n == null) || guard ? 1 : n); + }; + + // Trim out all falsy values from an array. + _.compact = function(array) { + return _.filter(array, _.identity); + }; + + // Internal implementation of a recursive `flatten` function. + var flatten = function(input, shallow, output) { + if (shallow && _.every(input, _.isArray)) { + return concat.apply(output, input); + } + each(input, function(value) { + if (_.isArray(value) || _.isArguments(value)) { + shallow ? push.apply(output, value) : flatten(value, shallow, output); + } else { + output.push(value); + } + }); + return output; + }; + + // Flatten out an array, either recursively (by default), or just one level. + _.flatten = function(array, shallow) { + return flatten(array, shallow, []); + }; + + // Return a version of the array that does not contain the specified value(s). + _.without = function(array) { + return _.difference(array, slice.call(arguments, 1)); + }; + + // Produce a duplicate-free version of the array. If the array has already + // been sorted, you have the option of using a faster algorithm. + // Aliased as `unique`. + _.uniq = _.unique = function(array, isSorted, iterator, context) { + if (_.isFunction(isSorted)) { + context = iterator; + iterator = isSorted; + isSorted = false; + } + var initial = iterator ? _.map(array, iterator, context) : array; + var results = []; + var seen = []; + each(initial, function(value, index) { + if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) { + seen.push(value); + results.push(array[index]); + } + }); + return results; + }; + + // Produce an array that contains the union: each distinct element from all of + // the passed-in arrays. + _.union = function() { + return _.uniq(_.flatten(arguments, true)); + }; + + // Produce an array that contains every item shared between all the + // passed-in arrays. + _.intersection = function(array) { + var rest = slice.call(arguments, 1); + return _.filter(_.uniq(array), function(item) { + return _.every(rest, function(other) { + return _.indexOf(other, item) >= 0; + }); + }); + }; + + // Take the difference between one array and a number of other arrays. + // Only the elements present in just the first array will remain. + _.difference = function(array) { + var rest = concat.apply(ArrayProto, slice.call(arguments, 1)); + return _.filter(array, function(value){ return !_.contains(rest, value); }); + }; + + // Zip together multiple lists into a single array -- elements that share + // an index go together. + _.zip = function() { + var length = _.max(_.pluck(arguments, "length").concat(0)); + var results = new Array(length); + for (var i = 0; i < length; i++) { + results[i] = _.pluck(arguments, '' + i); + } + return results; + }; + + // Converts lists into objects. Pass either a single array of `[key, value]` + // pairs, or two parallel arrays of the same length -- one of keys, and one of + // the corresponding values. + _.object = function(list, values) { + if (list == null) return {}; + var result = {}; + for (var i = 0, length = list.length; i < length; i++) { + if (values) { + result[list[i]] = values[i]; + } else { + result[list[i][0]] = list[i][1]; + } + } + return result; + }; + + // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**), + // we need this function. Return the position of the first occurrence of an + // item in an array, or -1 if the item is not included in the array. + // Delegates to **ECMAScript 5**'s native `indexOf` if available. + // If the array is large and already in sort order, pass `true` + // for **isSorted** to use binary search. + _.indexOf = function(array, item, isSorted) { + if (array == null) return -1; + var i = 0, length = array.length; + if (isSorted) { + if (typeof isSorted == 'number') { + i = (isSorted < 0 ? Math.max(0, length + isSorted) : isSorted); + } else { + i = _.sortedIndex(array, item); + return array[i] === item ? i : -1; + } + } + if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted); + for (; i < length; i++) if (array[i] === item) return i; + return -1; + }; + + // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available. + _.lastIndexOf = function(array, item, from) { + if (array == null) return -1; + var hasIndex = from != null; + if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) { + return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item); + } + var i = (hasIndex ? from : array.length); + while (i--) if (array[i] === item) return i; + return -1; + }; + + // Generate an integer Array containing an arithmetic progression. A port of + // the native Python `range()` function. See + // [the Python documentation](http://docs.python.org/library/functions.html#range). + _.range = function(start, stop, step) { + if (arguments.length <= 1) { + stop = start || 0; + start = 0; + } + step = arguments[2] || 1; + + var length = Math.max(Math.ceil((stop - start) / step), 0); + var idx = 0; + var range = new Array(length); + + while(idx < length) { + range[idx++] = start; + start += step; + } + + return range; + }; + + // Function (ahem) Functions + // ------------------ + + // Reusable constructor function for prototype setting. + var ctor = function(){}; + + // Create a function bound to a given object (assigning `this`, and arguments, + // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if + // available. + _.bind = function(func, context) { + var args, bound; + if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1)); + if (!_.isFunction(func)) throw new TypeError; + args = slice.call(arguments, 2); + return bound = function() { + if (!(this instanceof bound)) return func.apply(context, args.concat(slice.call(arguments))); + ctor.prototype = func.prototype; + var self = new ctor; + ctor.prototype = null; + var result = func.apply(self, args.concat(slice.call(arguments))); + if (Object(result) === result) return result; + return self; + }; + }; + + // Partially apply a function by creating a version that has had some of its + // arguments pre-filled, without changing its dynamic `this` context. + _.partial = function(func) { + var args = slice.call(arguments, 1); + return function() { + return func.apply(this, args.concat(slice.call(arguments))); + }; + }; + + // Bind a number of an object's methods to that object. Remaining arguments + // are the method names to be bound. Useful for ensuring that all callbacks + // defined on an object belong to it. + _.bindAll = function(obj) { + var funcs = slice.call(arguments, 1); + if (funcs.length === 0) throw new Error("bindAll must be passed function names"); + each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); }); + return obj; + }; + + // Memoize an expensive function by storing its results. + _.memoize = function(func, hasher) { + var memo = {}; + hasher || (hasher = _.identity); + return function() { + var key = hasher.apply(this, arguments); + return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments)); + }; + }; + + // Delays a function for the given number of milliseconds, and then calls + // it with the arguments supplied. + _.delay = function(func, wait) { + var args = slice.call(arguments, 2); + return setTimeout(function(){ return func.apply(null, args); }, wait); + }; + + // Defers a function, scheduling it to run after the current call stack has + // cleared. + _.defer = function(func) { + return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1))); + }; + + // Returns a function, that, when invoked, will only be triggered at most once + // during a given window of time. Normally, the throttled function will run + // as much as it can, without ever going more than once per `wait` duration; + // but if you'd like to disable the execution on the leading edge, pass + // `{leading: false}`. To disable execution on the trailing edge, ditto. + _.throttle = function(func, wait, options) { + var context, args, result; + var timeout = null; + var previous = 0; + options || (options = {}); + var later = function() { + previous = options.leading === false ? 0 : getTime(); + timeout = null; + result = func.apply(context, args); + context = args = null; + }; + return function() { + var now = getTime(); + if (!previous && options.leading === false) previous = now; + var remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + context = args = null; + } else if (!timeout && options.trailing !== false) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }; + + // Returns a function, that, as long as it continues to be invoked, will not + // be triggered. The function will be called after it stops being called for + // N milliseconds. If `immediate` is passed, trigger the function on the + // leading edge, instead of the trailing. + _.debounce = function(func, wait, immediate) { + var timeout, args, context, timestamp, result; + return function() { + context = this; + args = arguments; + timestamp = getTime(); + var later = function() { + var last = getTime() - timestamp; + if (last < wait) { + timeout = setTimeout(later, wait - last); + } else { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + context = args = null; + } + } + }; + var callNow = immediate && !timeout; + if (!timeout) { + timeout = setTimeout(later, wait); + } + if (callNow) { + result = func.apply(context, args); + context = args = null; + } + + return result; + }; + }; + + // Returns a function that will be executed at most one time, no matter how + // often you call it. Useful for lazy initialization. + _.once = function(func) { + var ran = false, memo; + return function() { + if (ran) return memo; + ran = true; + memo = func.apply(this, arguments); + func = null; + return memo; + }; + }; + + // Returns the first function passed as an argument to the second, + // allowing you to adjust arguments, run code before and after, and + // conditionally execute the original function. + _.wrap = function(func, wrapper) { + return _.partial(wrapper, func); + }; + + // Returns a function that is the composition of a list of functions, each + // consuming the return value of the function that follows. + _.compose = function() { + var funcs = arguments; + return function() { + var args = arguments; + for (var i = funcs.length - 1; i >= 0; i--) { + args = [funcs[i].apply(this, args)]; + } + return args[0]; + }; + }; + + // Returns a function that will only be executed after being called N times. + _.after = function(times, func) { + return function() { + if (--times < 1) { + return func.apply(this, arguments); + } + }; + }; + + // Object Functions + // ---------------- + + // Retrieve the names of an object's properties. + // Delegates to **ECMAScript 5**'s native `Object.keys` + _.keys = nativeKeys || function(obj) { + if (obj !== Object(obj)) throw new TypeError('Invalid object'); + var keys = []; + for (var key in obj) if (_.has(obj, key)) keys.push(key); + return keys; + }; + + // Retrieve the values of an object's properties. + _.values = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var values = new Array(length); + for (var i = 0; i < length; i++) { + values[i] = obj[keys[i]]; + } + return values; + }; + + // Convert an object into a list of `[key, value]` pairs. + _.pairs = function(obj) { + var keys = _.keys(obj); + var length = keys.length; + var pairs = new Array(length); + for (var i = 0; i < length; i++) { + pairs[i] = [keys[i], obj[keys[i]]]; + } + return pairs; + }; + + // Invert the keys and values of an object. The values must be serializable. + _.invert = function(obj) { + var result = {}; + var keys = _.keys(obj); + for (var i = 0, length = keys.length; i < length; i++) { + result[obj[keys[i]]] = keys[i]; + } + return result; + }; + + // Return a sorted list of the function names available on the object. + // Aliased as `methods` + _.functions = _.methods = function(obj) { + var names = []; + for (var key in obj) { + if (_.isFunction(obj[key])) names.push(key); + } + return names.sort(); + }; + + // Extend a given object with all the properties in passed-in object(s). + _.extend = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Return a copy of the object only containing the whitelisted properties. + _.pick = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + each(keys, function(key) { + if (key in obj) copy[key] = obj[key]; + }); + return copy; + }; + + // Return a copy of the object without the blacklisted properties. + _.omit = function(obj) { + var copy = {}; + var keys = concat.apply(ArrayProto, slice.call(arguments, 1)); + for (var key in obj) { + if (!_.contains(keys, key)) copy[key] = obj[key]; + } + return copy; + }; + + // Fill in a given object with default properties. + _.defaults = function(obj) { + each(slice.call(arguments, 1), function(source) { + if (source) { + for (var prop in source) { + if (obj[prop] === void 0) obj[prop] = source[prop]; + } + } + }); + return obj; + }; + + // Create a (shallow-cloned) duplicate of an object. + _.clone = function(obj) { + if (!_.isObject(obj)) return obj; + return _.isArray(obj) ? obj.slice() : _.extend({}, obj); + }; + + // Invokes interceptor with the obj, and then returns obj. + // The primary purpose of this method is to "tap into" a method chain, in + // order to perform operations on intermediate results within the chain. + _.tap = function(obj, interceptor) { + interceptor(obj); + return obj; + }; + + // Internal recursive comparison function for `isEqual`. + var eq = function(a, b, aStack, bStack) { + // Identical objects are equal. `0 === -0`, but they aren't identical. + // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal). + if (a === b) return a !== 0 || 1 / a == 1 / b; + // A strict comparison is necessary because `null == undefined`. + if (a == null || b == null) return a === b; + // Unwrap any wrapped objects. + if (a instanceof _) a = a._wrapped; + if (b instanceof _) b = b._wrapped; + // Compare `[[Class]]` names. + var className = toString.call(a); + if (className != toString.call(b)) return false; + switch (className) { + // Strings, numbers, dates, and booleans are compared by value. + case '[object String]': + // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is + // equivalent to `new String("5")`. + return a == String(b); + case '[object Number]': + // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for + // other numeric values. + return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b); + case '[object Date]': + case '[object Boolean]': + // Coerce dates and booleans to numeric primitive values. Dates are compared by their + // millisecond representations. Note that invalid dates with millisecond representations + // of `NaN` are not equivalent. + return +a == +b; + // RegExps are compared by their source patterns and flags. + case '[object RegExp]': + return a.source == b.source && + a.global == b.global && + a.multiline == b.multiline && + a.ignoreCase == b.ignoreCase; + } + if (typeof a != 'object' || typeof b != 'object') return false; + // Assume equality for cyclic structures. The algorithm for detecting cyclic + // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`. + var length = aStack.length; + while (length--) { + // Linear search. Performance is inversely proportional to the number of + // unique nested structures. + if (aStack[length] == a) return bStack[length] == b; + } + // Objects with different constructors are not equivalent, but `Object`s + // from different frames are. + var aCtor = a.constructor, bCtor = b.constructor; + if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) && + _.isFunction(bCtor) && (bCtor instanceof bCtor)) + && ('constructor' in a && 'constructor' in b)) { + return false; + } + // Add the first object to the stack of traversed objects. + aStack.push(a); + bStack.push(b); + var size = 0, result = true; + // Recursively compare objects and arrays. + if (className == '[object Array]') { + // Compare array lengths to determine if a deep comparison is necessary. + size = a.length; + result = size == b.length; + if (result) { + // Deep compare the contents, ignoring non-numeric properties. + while (size--) { + if (!(result = eq(a[size], b[size], aStack, bStack))) break; + } + } + } else { + // Deep compare objects. + for (var key in a) { + if (_.has(a, key)) { + // Count the expected number of properties. + size++; + // Deep compare each member. + if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break; + } + } + // Ensure that both objects contain the same number of properties. + if (result) { + for (key in b) { + if (_.has(b, key) && !(size--)) break; + } + result = !size; + } + } + // Remove the first object from the stack of traversed objects. + aStack.pop(); + bStack.pop(); + return result; + }; + + // Perform a deep comparison to check if two objects are equal. + _.isEqual = function(a, b) { + return eq(a, b, [], []); + }; + + // Is a given array, string, or object empty? + // An "empty" object has no enumerable own-properties. + _.isEmpty = function(obj) { + if (obj == null) return true; + if (_.isArray(obj) || _.isString(obj)) return obj.length === 0; + for (var key in obj) if (_.has(obj, key)) return false; + return true; + }; + + // Is a given value a DOM element? + _.isElement = function(obj) { + return !!(obj && obj.nodeType === 1); + }; + + // Is a given value an array? + // Delegates to ECMA5's native Array.isArray + _.isArray = nativeIsArray || function(obj) { + return toString.call(obj) == '[object Array]'; + }; + + // Is a given variable an object? + _.isObject = function(obj) { + return obj === Object(obj); + }; + + // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp. + each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) { + _['is' + name] = function(obj) { + return toString.call(obj) == '[object ' + name + ']'; + }; + }); + + // Define a fallback version of the method in browsers (ahem, IE), where + // there isn't any inspectable "Arguments" type. + if (!_.isArguments(arguments)) { + _.isArguments = function(obj) { + return !!(obj && _.has(obj, 'callee')); + }; + } + + // Optimize `isFunction` if appropriate. + if (typeof (/./) !== 'function') { + _.isFunction = function(obj) { + return typeof obj === 'function'; + }; + } + + // Is a given object a finite number? + _.isFinite = function(obj) { + return isFinite(obj) && !isNaN(parseFloat(obj)); + }; + + // Is the given value `NaN`? (NaN is the only number which does not equal itself). + _.isNaN = function(obj) { + return _.isNumber(obj) && obj != +obj; + }; + + // Is a given value a boolean? + _.isBoolean = function(obj) { + return obj === true || obj === false || toString.call(obj) == '[object Boolean]'; + }; + + // Is a given value equal to null? + _.isNull = function(obj) { + return obj === null; + }; + + // Is a given variable undefined? + _.isUndefined = function(obj) { + return obj === void 0; + }; + + // Shortcut function for checking if an object has a given property directly + // on itself (in other words, not on a prototype). + _.has = function(obj, key) { + return hasOwnProperty.call(obj, key); + }; + + // Utility Functions + // ----------------- + + // Run Underscore.js in *noConflict* mode, returning the `_` variable to its + // previous owner. Returns a reference to the Underscore object. + _.noConflict = function() { + root._ = previousUnderscore; + return this; + }; + + // Keep the identity function around for default iterators. + _.identity = function(value) { + return value; + }; + + _.constant = function(value) { + return function () { + return value; + }; + }; + + _.property = function(key) { + return function(obj) { + return obj[key]; + }; + }; + + // Run a function **n** times. + _.times = function(n, iterator, context) { + var accum = Array(Math.max(0, n)); + for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i); + return accum; + }; + + // Return a random integer between min and max (inclusive). + _.random = function(min, max) { + if (max == null) { + max = min; + min = 0; + } + return min + Math.floor(Math.random() * (max - min + 1)); + }; + + // List of HTML entities for escaping. + var entityMap = { + escape: { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + } + }; + entityMap.unescape = _.invert(entityMap.escape); + + // Regexes containing the keys and values listed immediately above. + var entityRegexes = { + escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'), + unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g') + }; + + // Functions for escaping and unescaping strings to/from HTML interpolation. + _.each(['escape', 'unescape'], function(method) { + _[method] = function(string) { + if (string == null) return ''; + return ('' + string).replace(entityRegexes[method], function(match) { + return entityMap[method][match]; + }); + }; + }); + + // If the value of the named `property` is a function then invoke it with the + // `object` as context; otherwise, return it. + _.result = function(object, property) { + if (object == null) return void 0; + var value = object[property]; + return _.isFunction(value) ? value.call(object) : value; + }; + + // Add your own custom functions to the Underscore object. + _.mixin = function(obj) { + each(_.functions(obj), function(name) { + var func = _[name] = obj[name]; + _.prototype[name] = function() { + var args = [this._wrapped]; + push.apply(args, arguments); + return result.call(this, func.apply(_, args)); + }; + }); + }; + + // Generate a unique integer id (unique within the entire client session). + // Useful for temporary DOM ids. + var idCounter = 0; + _.uniqueId = function(prefix) { + var id = ++idCounter + ''; + return prefix ? prefix + id : id; + }; + + // By default, Underscore uses ERB-style template delimiters, change the + // following template settings to use alternative delimiters. + _.templateSettings = { + evaluate : /<%([\s\S]+?)%>/g, + interpolate : /<%=([\s\S]+?)%>/g, + escape : /<%-([\s\S]+?)%>/g + }; + + // When customizing `templateSettings`, if you don't want to define an + // interpolation, evaluation or escaping regex, we need one that is + // guaranteed not to match. + var noMatch = /(.)^/; + + // Certain characters need to be escaped so that they can be put into a + // string literal. + var escapes = { + "'": "'", + '\\': '\\', + '\r': 'r', + '\n': 'n', + '\t': 't', + '\u2028': 'u2028', + '\u2029': 'u2029' + }; + + var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g; + + // JavaScript micro-templating, similar to John Resig's implementation. + // Underscore templating handles arbitrary delimiters, preserves whitespace, + // and correctly escapes quotes within interpolated code. + _.template = function(text, data, settings) { + var render; + settings = _.defaults({}, settings, _.templateSettings); + + // Combine delimiters into one regular expression via alternation. + var matcher = new RegExp([ + (settings.escape || noMatch).source, + (settings.interpolate || noMatch).source, + (settings.evaluate || noMatch).source + ].join('|') + '|$', 'g'); + + // Compile the template source, escaping string literals appropriately. + var index = 0; + var source = "__p+='"; + text.replace(matcher, function(match, escape, interpolate, evaluate, offset) { + source += text.slice(index, offset) + .replace(escaper, function(match) { return '\\' + escapes[match]; }); + + if (escape) { + source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'"; + } + if (interpolate) { + source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'"; + } + if (evaluate) { + source += "';\n" + evaluate + "\n__p+='"; + } + index = offset + match.length; + return match; + }); + source += "';\n"; + + // If a variable is not specified, place data values in local scope. + if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n'; + + source = "var __t,__p='',__j=Array.prototype.join," + + "print=function(){__p+=__j.call(arguments,'');};\n" + + source + "return __p;\n"; + + try { + render = new Function(settings.variable || 'obj', '_', source); + } catch (e) { + e.source = source; + throw e; + } + + if (data) return render(data, _); + var template = function(data) { + return render.call(this, data, _); + }; + + // Provide the compiled function source as a convenience for precompilation. + template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}'; + + return template; + }; + + // Add a "chain" function, which will delegate to the wrapper. + _.chain = function(obj) { + return _(obj).chain(); + }; + + // OOP + // --------------- + // If Underscore is called as a function, it returns a wrapped object that + // can be used OO-style. This wrapper holds altered versions of all the + // underscore functions. Wrapped objects may be chained. + + // Helper function to continue chaining intermediate results. + var result = function(obj) { + return this._chain ? _(obj).chain() : obj; + }; + + // Add all of the Underscore functions to the wrapper object. + _.mixin(_); + + // Add all mutator Array functions to the wrapper. + each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + var obj = this._wrapped; + method.apply(obj, arguments); + if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0]; + return result.call(this, obj); + }; + }); + + // Add all accessor Array functions to the wrapper. + each(['concat', 'join', 'slice'], function(name) { + var method = ArrayProto[name]; + _.prototype[name] = function() { + return result.call(this, method.apply(this._wrapped, arguments)); + }; + }); + + _.extend(_.prototype, { + + // Start chaining a wrapped Underscore object. + chain: function() { + this._chain = true; + return this; + }, + + // Extracts the result from a wrapped and chained object. + value: function() { + return this._wrapped; + } + + }); + + // AMD registration happens at the end for compatibility with AMD loaders + // that may not enforce next-turn semantics on modules. Even though general + // practice for AMD registration is to be anonymous, underscore registers + // as a named module because, like jQuery, it is a base library that is + // popular enough to be bundled in a third party lib, but not be part of + // an AMD load request. Those cases could generate an error when an + // anonymous define() is called outside of a loader request. + if (typeof define === 'function' && define.amd) { + define('underscore', [], function() { + return _; + }); + } +}).call(this); diff --git a/src/main/javascript/modules/utils/package.json b/src/main/javascript/modules/utils/package.json new file mode 100644 index 0000000..d80809d --- /dev/null +++ b/src/main/javascript/modules/utils/package.json @@ -0,0 +1,4 @@ +{ + "name": 'utils', + "main": './utils.js' +} diff --git a/src/main/javascript/utils/text.js b/src/main/javascript/modules/utils/string-exts.js similarity index 100% rename from src/main/javascript/utils/text.js rename to src/main/javascript/modules/utils/string-exts.js diff --git a/src/main/javascript/utils/utils.js b/src/main/javascript/modules/utils/utils.js similarity index 100% rename from src/main/javascript/utils/utils.js rename to src/main/javascript/modules/utils/utils.js diff --git a/src/main/javascript/alias/alias.js b/src/main/javascript/plugins/alias/alias.js similarity index 100% rename from src/main/javascript/alias/alias.js rename to src/main/javascript/plugins/alias/alias.js diff --git a/src/main/javascript/arrows/arrows.js b/src/main/javascript/plugins/arrows.js similarity index 100% rename from src/main/javascript/arrows/arrows.js rename to src/main/javascript/plugins/arrows.js diff --git a/src/main/javascript/chat/color.js b/src/main/javascript/plugins/chat/color.js similarity index 100% rename from src/main/javascript/chat/color.js rename to src/main/javascript/plugins/chat/color.js diff --git a/src/main/javascript/classroom/classroom.js b/src/main/javascript/plugins/classroom/classroom.js similarity index 100% rename from src/main/javascript/classroom/classroom.js rename to src/main/javascript/plugins/classroom/classroom.js diff --git a/src/main/javascript/plugins/drone/blocks.js b/src/main/javascript/plugins/drone/blocks.js new file mode 100644 index 0000000..19e0c0c --- /dev/null +++ b/src/main/javascript/plugins/drone/blocks.js @@ -0,0 +1,275 @@ +/************************************************************************ +Blocks Module +============= +You hate having to lookup [Data Values][dv] when you use ScriptCraft's Drone() functions. So do I. +So I created this blocks object which is a helper object for use in construction. + +Examples +-------- + + box( blocks.oak ); // creates a single oak wood block + box( blocks.sand, 3, 2, 1 ); // creates a block of sand 3 wide x 2 high x 1 long + box( blocks.wool.green, 2 ); // creates a block of green wool 2 blocks wide + +Color aliased properties that were a direct descendant of the blocks object are no longer used to avoid confusion with carpet and stained clay blocks. In addition, there's a convenience array `blocks.rainbow` which is an array of the 7 colors of the rainbow (or closest approximations). + +***/ +var blocks = { + air: 0, + stone: 1, + grass: 2, + dirt: 3, + cobblestone: 4, + oak: 5, + spruce: '5:1', + birch: '5:2', + jungle: '5:3', + sapling: { + oak: 6, + spruce: '6:1', + birch: '62:2', + jungle: '6:3' + }, + bedrock: 7, + water: 8, + water_still: 9, + lava: 10, + lava_still: 11, + sand: 12, + gravel: 13, + gold_ore: 14, + iron_ore: 15, + coal_ore: 16, + wood: 17, + leaves: 18, + sponge: 19, + glass: 20, + lapis_lazuli_ore: 21, + lapis_lazuli_block: 22, + dispenser: 23, + sandstone: 24, + note: 25, + bed: 26, + powered_rail: 27, + detector_rail: 28, + sticky_piston: 29, + cobweb: 30, + grass_tall: 31, + dead_bush: 32, + piston: 33, + piston_extn: 34, + wool: { + white: 35 // All other colors added below + }, + dandelion: 37, + flower_yellow: 37, + rose: 38, + flower_red: 38, + mushroom_brown: 39, + mushroom_red: 40, + gold: 41, + iron: 42, + tnt: 46, + bookshelf: 47, + moss_stone: 48, + obsidian: 49, + torch: 50, + fire: 51, + monster_spawner: 52, + stairs: { + oak: 53, + cobblestone: 67, + brick: 108, + stone: 109, + nether: 114, + sandstone: 128, + spruce: 134, + birch: 135, + jungle: 136, + quartz: 156 + }, + chest: 54, + redstone_wire: 55, + diamond_ore: 56, + diamond: 57, + crafting_table: 58, + wheat_seeds: 59, + farmland: 60, + furnace: 61, + furnace_burning: 62, + sign_post: 63, + door_wood: 64, + ladder: 65, + rail: 66, + sign: 68, + lever: 69, + pressure_plate_stone: 70, + door_iron: 71, + pressure_plate_wood: 72, + redstone_ore: 73, + redstone_ore_glowing: 74, + torch_redstone: 75, + torch_redstone_active: 76, + stone_button: 77, + ice: 79, + snow: 80, + cactus: 81, + clay: 82, + sugar_cane: 83, + jukebox: 84, + fence: 85, + pumpkin: 86, + netherrack: 87, + soulsand: 88, + glowstone: 89, + netherportal: 90, + jackolantern: 91, + cake: 92, + redstone_repeater: 93, + redeston_repeater_active: 94, + chest_locked: 95, + trapdoor: 96, + monster_egg: 97, + brick: { + stone: 98, + mossy: '98:1', + cracked: '98:2', + chiseled: '98:3', + red: 45 + }, + mushroom_brown_huge: 99, + mushroom_red_huge: 100, + iron_bars: 101, + glass_pane: 102, + melon: 103, + pumpkin_stem: 104, + melon_stem: 105, + vines: 106, + fence_gate: 107, + mycelium: 110, + lily_pad: 111, + nether: 112, + nether_fence: 113, + netherwart: 115, + table_enchantment: 116, + brewing_stand: 117, + cauldron: 118, + endportal: 119, + endportal_frame: 120, + endstone: 121, + dragon_egg: 122, + redstone_lamp: 123, + redstone_lamp_active: 124, + slab: { + snow: 78, + stone: 44, + sandstone: '44:1', + wooden: '44:2', + cobblestone: '44:3', + brick: '44:4', + stonebrick: '44:5', + netherbrick:'44:6', + quartz: '44:7', + oak: 126, + spruce: '126:1', + birch: '126:2', + jungle: '126:3', + upper: { + stone: '44:8', + sandstone: '44:9', + wooden: '44:10', + cobblestone: '44:11', + brick: '44:12', + stonebrick: '44:13', + netherbrick:'44:14', + quartz: '44:15', + oak: '126:8', + spruce: '126:9', + birch: '126:10', + jungle: '126:11', + } + }, + cocoa: 127, + emerald_ore: 129, + enderchest: 130, + tripwire_hook: 131, + tripwire: 132, + emerald: 133, + command: 137, + beacon: 138, + cobblestone_wall: 139, + flowerpot: 140, + carrots: 141, + potatoes: 142, + button_wood: 143, + mobhead: 144, + anvil: 145, + chest_trapped: 146, + pressure_plate_weighted_light: 147, + pressure_plate_weighted_heavy: 148, + redstone_comparator: 149, + redstone_comparator_active: 150, + daylight_sensor: 151, + redstone: 152, + netherquartzore: 153, + hopper: 154, + quartz: 155, + rail_activator: 157, + dropper: 158, + stained_clay: { + white: 159 // All other colors added below + }, + hay: 170, + carpet: { + white: 171 // All other colors added below + }, + hardened_clay: 172, + coal_block: 173 +}; + +// Add all available colors to colorized block collections + +var colors = { + orange: ':1', + magenta: ':2', + lightblue: ':3', + yellow: ':4', + lime: ':5', + pink: ':6', + gray: ':7', + lightgray: ':8', + cyan: ':9', + purple: ':10', + blue: ':11', + brown: ':12', + green: ':13', + red: ':14', + black: ':15' +}; +var colorized_blocks = ["wool", "stained_clay", "carpet"]; + +for (var i = 0, len = colorized_blocks.length; i < len; i++) { + var block = colorized_blocks[i], + data_value = blocks[block].white; + + for (var color in colors) { + blocks[block][color] = data_value + colors[color]; + } +}; + +/* + rainbow colors - a convenience + Color aliased properties that were a direct descendant of the blocks + object are no longer used to avoid confusion with carpet and stained + clay blocks. +*/ +blocks.rainbow = [blocks.wool.red, + blocks.wool.orange, + blocks.wool.yellow, + blocks.wool.lime, + blocks.wool.lightblue, + blocks.wool.blue, + blocks.wool.purple]; + + +module.exports = blocks; diff --git a/src/main/javascript/plugins/drone/blocktype.js b/src/main/javascript/plugins/drone/blocktype.js new file mode 100644 index 0000000..1752280 --- /dev/null +++ b/src/main/javascript/plugins/drone/blocktype.js @@ -0,0 +1,376 @@ +var Drone = require('./drone'); +var blocks = require('./blocks'); + +module.exports = Drone; +/************************************************************************ +Drone.blocktype() method +======================== +Creates the text out of blocks. Useful for large-scale in-game signs. + +Parameters +---------- + + * message - The message to create - (use `\n` for newlines) + * foregroundBlock (default: black wool) - The block to use for the foreground + * backgroundBlock (default: none) - The block to use for the background + +Example +------- +To create a 2-line high message using glowstone... + + blocktype("Hello\nWorld",blocks.glowstone); + +![blocktype example][imgbt1] + +[imgbt1]: img/blocktype1.png + +***/ + +var bitmaps = { + raw: { + '0':' ### '+ + ' # # '+ + ' # # '+ + ' # # '+ + ' ### ', + + '1':' # '+ + ' ## '+ + ' # '+ + ' # '+ + ' ### ', + + '2':' ### '+ + ' # '+ + ' ### '+ + ' # '+ + ' ### ', + + '3':' ### '+ + ' # '+ + ' ## '+ + ' # '+ + ' ### ', + + '4':' # '+ + ' ## '+ + ' # # '+ + ' ### '+ + ' # ', + + '5':' ### '+ + ' # '+ + ' ### '+ + ' # '+ + ' ### ', + + '6':' ### '+ + ' # '+ + ' ### '+ + ' # # '+ + ' ### ', + + '7':' ### '+ + ' # '+ + ' # '+ + ' # '+ + ' # ', + + '8':' ### '+ + ' # # '+ + ' ### '+ + ' # # '+ + ' ### ', + + '9':' ### '+ + ' # # '+ + ' ### '+ + ' # '+ + ' ### ', + + 'a':' ### '+ + ' # # '+ + ' ### '+ + ' # # '+ + ' # # ', + + 'b':' ## '+ + ' # # '+ + ' ## '+ + ' # # '+ + ' ## ', + + 'c':' ## '+ + ' # '+ + ' # '+ + ' # '+ + ' ## ', + + 'd':' ## '+ + ' # # '+ + ' # # '+ + ' # # '+ + ' ## ', + + 'e':' ### '+ + ' # '+ + ' ## '+ + ' # '+ + ' ### ', + + 'f':' ### '+ + ' # '+ + ' ## '+ + ' # '+ + ' # ', + + 'g':' ### '+ + ' # '+ + ' # '+ + ' # # '+ + ' ### ', + + 'h':' # # '+ + ' # # '+ + ' ### '+ + ' # # '+ + ' # # ', + + 'i':' ### '+ + ' # '+ + ' # '+ + ' # '+ + ' ### ', + + 'j':' ### '+ + ' # '+ + ' # '+ + ' # '+ + ' # ', + + 'k':' # '+ + ' # # '+ + ' ## '+ + ' # # '+ + ' # # ', + + 'l':' # '+ + ' # '+ + ' # '+ + ' # '+ + ' ### ', + + 'm':' # # '+ + ' ### '+ + ' # # '+ + ' # # '+ + ' # # ', + + 'n':' ## '+ + ' # # '+ + ' # # '+ + ' # # '+ + ' # # ', + + 'o':' # '+ + ' # # '+ + ' # # '+ + ' # # '+ + ' # ', + + 'p':' ### '+ + ' # # '+ + ' ### '+ + ' # '+ + ' # ', + + 'q':' ### '+ + ' # # '+ + ' # # '+ + ' ### '+ + ' # ', + + 'r':' ## '+ + ' # # '+ + ' ## '+ + ' # # '+ + ' # # ', + + 's':' ## '+ + ' # '+ + ' ### '+ + ' # '+ + ' ## ', + + 't':' ### '+ + ' # '+ + ' # '+ + ' # '+ + ' # ', + + 'u':' # # '+ + ' # # '+ + ' # # '+ + ' # # '+ + ' ### ', + + 'v':' # # '+ + ' # # '+ + ' # # '+ + ' # # '+ + ' # ', + + 'w':' # # '+ + ' # # '+ + ' # # '+ + ' ### '+ + ' # # ', + + 'x':' # # '+ + ' # # '+ + ' # '+ + ' # # '+ + ' # # ', + + 'y':' # # '+ + ' # # '+ + ' # # '+ + ' # '+ + ' # ', + + 'z':' ### '+ + ' # '+ + ' # '+ + ' # '+ + ' ### ', + + '!':' # '+ + ' # '+ + ' # '+ + ' '+ + ' # ', + + ':':' '+ + ' # '+ + ' '+ + ' # '+ + ' ', + + ';':' '+ + ' # '+ + ' '+ + ' # '+ + ' # ', + + ',':' '+ + ' '+ + ' '+ + ' # '+ + ' # ', + + '/':' # '+ + ' # '+ + ' # '+ + ' # '+ + ' # ', + + '+':' '+ + ' # '+ + ' ### '+ + ' # '+ + ' ', + + '-':' '+ + ' '+ + ' ### '+ + ' '+ + ' ', + + '.':' '+ + ' '+ + ' '+ + ' '+ + ' # ', + + "'":' # '+ + ' # '+ + ' '+ + ' '+ + ' ', + + ' ':' '+ + ' '+ + ' '+ + ' '+ + ' ' + }, + computed: {} +}; +/* + wph 20130121 compute the width, and x,y coords of pixels ahead of time +*/ +for (var c in bitmaps.raw){ + var bits = bitmaps.raw[c]; + var width = bits.length/5; + var bmInfo = {"width": width,"pixels":[]} + bitmaps.computed[c] = bmInfo; + for (var j = 0; j < bits.length; j++){ + if (bits.charAt(j) != ' '){ + bmInfo.pixels.push([j%width,Math.ceil(j/width)]); + } + } +} + + +// +// message +// string with text to be displayed +// fg +// foreground material. The material the text will be in. +// bg +// background material, optional. The negative space within the bounding box of the text. +// +Drone.extend('blocktype', function(message,fg,bg){ + + this.chkpt('blocktext'); + + if (typeof fg == "undefined") + fg = blocks.wool.black; + + var bmfg = this._getBlockIdAndMeta(fg); + var bmbg = null; + if (typeof bg != "undefined") + bmbg = this._getBlockIdAndMeta(bg); + var lines = message.split("\n"); + var lineCount = lines.length; + for (var h = 0;h < lineCount; h++) { + var line = lines[h]; + line = line.toLowerCase().replace(/[^0-9a-z \.\-\+\/\;\'\:\!]/g,""); + this.up(7*(lineCount-(h+1))); + + for (var i =0;i < line.length; i++) { + var ch = line.charAt(i) + var bits = bitmaps.computed[ch]; + if (typeof bits == "undefined"){ + bits = bitmaps.computed[' ']; + } + var charWidth = bits.width; + if (typeof bg != "undefined") + this.cuboidX(bmbg[0],bmbg[1],charWidth,7,1); + for (var j = 0;j < bits.pixels.length;j++){ + this.chkpt('btbl'); + var x = bits.pixels[j][0]; + var y = bits.pixels[j][1]; + this.up(6-y).right(x).cuboidX(bmfg[0],bmfg[1]); + this.move('btbl'); + } + this.right(charWidth-1); + } + this.move('blocktext'); + } + + return this.move('blocktext'); +}); + + + diff --git a/src/main/javascript/plugins/drone/contrib/castle.js b/src/main/javascript/plugins/drone/contrib/castle.js new file mode 100644 index 0000000..bd634c6 --- /dev/null +++ b/src/main/javascript/plugins/drone/contrib/castle.js @@ -0,0 +1,51 @@ +var Drone = require('../drone'); +module.exports = Drone; +// +// a castle is just a big wide fort with 4 taller forts at each corner +// +Drone.extend('castle', function(side, height) +{ + // + // use sensible default parameter values + // if no parameters are supplied + // + if (typeof side == "undefined") + side = 24; + if (typeof height == "undefined") + height = 10; + if (height < 8 || side < 20) + throw new java.lang.RuntimeException("Castles must be at least 20 wide X 8 tall"); + // + // remember where the drone is so it can return 'home' + // + this.chkpt('castle'); + // + // how big the towers at each corner will be... + // + var towerSide = 10; + var towerHeight = height+4; + + // + // the main castle building will be front and right of the first tower + // + this.fwd(towerSide/2).right(towerSide/2); + // + // the castle is really just a big fort with 4 smaller 'tower' forts at each corner + // + this.fort(side,height); + // + // move back to start position + // + this.move('castle'); + // + // now place 4 towers at each corner (each tower is another fort) + // + for (var corner = 0; corner < 4; corner++) + { + // construct a 'tower' fort + this.fort(towerSide,towerHeight); + // move forward the length of the castle then turn right + this.fwd(side+towerSide-1).turn(); + } + return this.move('castle'); +}); diff --git a/src/main/javascript/plugins/drone/contrib/chessboard.js b/src/main/javascript/plugins/drone/contrib/chessboard.js new file mode 100644 index 0000000..0987e01 --- /dev/null +++ b/src/main/javascript/plugins/drone/contrib/chessboard.js @@ -0,0 +1,37 @@ +var Drone = require('../drone'); +var blocks = require('../blocks'); + +module.exports = Drone; +/** +* Creates a tile pattern of given block types and size +* +* Paramters: +* whiteBlock - blockId used for the traditional white portion of the chessboard +* blackBlock - blockId used for the traditional black portion of the chessboard +* width - width of the chessboard +* height - height of the chessboard +*/ +Drone.extend("chessboard", function(whiteBlock, blackBlock, width, depth) { + this.chkpt('chessboard-start'); + if (typeof whiteBlock == "undefined") + whiteBlock = blocks.wool.white; + if (typeof blackBlock == "undefined") + blackBlock = blocks.wool.black; + if (typeof width == "undefined") + width = 8; + if (typeof depth == "undefined") + depth = width; + + for(var i = 0; i < width; ++i) { + for(var j = 0; j < depth; ++j) { + var block = blackBlock; + if((i+j)%2 == 1) { + block = whiteBlock; + } + this.box(block); + this.right(); + } + this.move('chessboard-start').fwd(i+1); + } + return this.move('chessboard-start'); +}); diff --git a/src/main/javascript/plugins/drone/contrib/cottage.js b/src/main/javascript/plugins/drone/contrib/cottage.js new file mode 100644 index 0000000..194f216 --- /dev/null +++ b/src/main/javascript/plugins/drone/contrib/cottage.js @@ -0,0 +1,79 @@ +var Drone = require('../drone'); +module.exports = Drone; +// +// usage: +// [1] to build a cottage at the player's current location or the cross-hairs location... +// +// /js cottage(); +// +// [2] to build a cottage using an existing drone... +// +// /js drone.cottage(); +// + +Drone.extend('cottage',function () +{ + this.chkpt('cottage') + .box0(48,7,2,6) // 4 walls + .right(3).door() // door front and center + .up(1).left(2).box(102) // windows to left and right + .right(4).box(102) + .left(5).up().prism0(53,7,6); + // + // put up a sign near door. + // + this.down().right(4).sign(["Home","Sweet","Home"],68); + + return this.move('cottage'); +}); +// +// a more complex script that builds an tree-lined avenue with +// cottages on both sides. +// +Drone.extend('cottage_road', function(numberCottages) +{ + if (typeof numberCottages == "undefined"){ + numberCottages = 6; + } + var i=0, distanceBetweenTrees = 11; + // + // step 1 build the road. + // + var cottagesPerSide = Math.floor(numberCottages/2); + this + .chkpt("cottage_road") // make sure the drone's state is saved. + .box(43,3,1,cottagesPerSide*(distanceBetweenTrees+1)) // build the road + .up().right() // now centered in middle of road + .chkpt("cr"); // will be returning to this position later + + // + // step 2 line the road with trees + // + for (; i < cottagesPerSide+1;i++){ + this + .left(5).oak() + .right(10).oak() + .left(5) // return to middle of road + .fwd(distanceBetweenTrees+1); // move forward. + } + this.move("cr").back(6); // move back 1/2 the distance between trees + + // this function builds a path leading to a cottage. + function pathAndCottage(d){ + return d.down().box(43,1,1,5).fwd(5).left(3).up().cottage(); + }; + // + // step 3 build cottages on each side + // + for (i = 0;i < cottagesPerSide; i++) + { + this.fwd(distanceBetweenTrees+1).chkpt("r"+i); + // build cottage on left + pathAndCottage(this.turn(3)).move("r"+i); + // build cottage on right + pathAndCottage(this.turn()).move("r"+i); + } + // return drone to where it was at start of function + return this.move("cottage_road"); +}); + diff --git a/src/main/javascript/plugins/drone/contrib/dancefloor.js b/src/main/javascript/plugins/drone/contrib/dancefloor.js new file mode 100644 index 0000000..6979186 --- /dev/null +++ b/src/main/javascript/plugins/drone/contrib/dancefloor.js @@ -0,0 +1,38 @@ +var Drone = require('../drone'); +module.exports = Drone; +// +// Create a floor of colored tiles some of which emit light. +// The tiles change color every second creating a strobe-lit dance-floor. +// +// See it in action here => http://www.youtube.com/watch?v=UEooBt6NTFo +// +Drone.extend('dancefloor',function(width,length) +{ + if (typeof width == "undefined") + width = 5; + if (typeof length == "undefined") + length = width; + // + // create a separate Drone object to lay down disco tiles + // + var disco = new Drone(this.x, this.y, this.z, this.dir, this.world); + // + // under-floor lighting + // + disco.down().box(89,width,1,length).up(); + var floorTiles = [35,35,'35:1','35:2','35:3','35:4','35:4','35:4','35:6',20,20]; + // + // strobe gets called in a java thread - disco only lasts 30 seconds. + // + var discoTicks = 30; + var task = null; + var strobe = function() { + disco.rand(floorTiles,width,1,length); + if (!discoTicks--) + task.cancel(); + }; + var now = 0; + var everySecond = 20; + task = server.scheduler.runTaskTimer(__plugin,strobe,now,everySecond); + return this; +}); diff --git a/src/main/javascript/plugins/drone/contrib/fort.js b/src/main/javascript/plugins/drone/contrib/fort.js new file mode 100644 index 0000000..0f43043 --- /dev/null +++ b/src/main/javascript/plugins/drone/contrib/fort.js @@ -0,0 +1,68 @@ +var Drone = require('../drone'); +module.exports = Drone; +// +// constructs a medieval fort +// +Drone.extend('fort', function(side, height) +{ + if (typeof side == "undefined") + side = 18; + if (typeof height == "undefined") + height = 6; + // make sure side is even + if (side%2) + side++; + if (height < 4 || side < 10) + throw new java.lang.RuntimeException("Forts must be at least 10 wide X 4 tall"); + var brick = 98; + // + // build walls. + // + this.chkpt('fort').box0(brick,side,height-1,side); + // + // build battlements + // + this.up(height-1); + for (i = 0;i <= 3;i++){ + var turret = []; + this.box(brick) // solid brick corners + .up().box('50:5').down() // light a torch on each corner + .fwd(); + turret.push('109:'+ Drone.PLAYER_STAIRS_FACING[this.dir]); + turret.push('109:'+ Drone.PLAYER_STAIRS_FACING[(this.dir+2)%4]); + try{ + this.boxa(turret,1,1,side-2).fwd(side-2).turn(); + }catch(e){ + self.sendMessage("ERROR: " + e.toString()); + } + } + // + // build battlement's floor + // + this.move('fort'); + this.up(height-2).fwd().right().box('126:0',side-2,1,side-2); + var battlementWidth = 3; + if (side <= 12) + battlementWidth = 2; + + this.fwd(battlementWidth).right(battlementWidth) + .box(0,side-((1+battlementWidth)*2),1,side-((1+battlementWidth)*2)); + // + // add door + // + var torch = '50:' + Drone.PLAYER_TORCH_FACING[this.dir]; + this.move('fort').right((side/2)-1).door2() // double doors + .back().left().up() + .box(torch) // left torch + .right(3) + .box(torch); // right torch + // + // add ladder up to battlements + // + var ladder = '65:' + Drone.PLAYER_SIGN_FACING[(this.dir+2)%4]; + this.move('fort').right((side/2)-3).fwd(1) // move inside fort + .box(ladder, 1,height-1,1); + return this.move('fort'); + +}); + diff --git a/src/main/javascript/plugins/drone/contrib/logo.js b/src/main/javascript/plugins/drone/contrib/logo.js new file mode 100644 index 0000000..d9507e6 --- /dev/null +++ b/src/main/javascript/plugins/drone/contrib/logo.js @@ -0,0 +1,219 @@ +var Drone = require('../drone'); +module.exports = Drone; +// +// Constructs the JS logo +// https://raw.github.com/voodootikigod/logo.js/master/js.png +// +// fg +// the material that the letters JS will be made of +// bg +// the material that the square will be made of +// +Drone.extend('logojs', function(fg, bg) { + + // foreground defaults to gray wool + if (typeof fg == "undefined") + fg = '35:7'; + // background defaults to gold blocks + if (typeof bg == "undefined") + bg = 41; + + // Draw the sqaure + this.chkpt('logojs-start') + .up() + .box(bg, 100, 100, 1); + + // Draw the J, starting with the hook + this.right(30).up(13) + .box(fg) + .right().down() + .box(fg, 1, 3, 1) + .right().down() + .box(fg, 1, 5, 1) + .right().down() + .box(fg, 1, 7, 1) + .right() + .box(fg, 1, 8, 1) + .right().down() + .box(fg, 1, 10, 1) + .right() + .box(fg, 1, 9, 1) + .right() + .box(fg, 1, 8, 1) + .right().down() + .box(fg, 2, 8, 1) + .right(2) + .box(fg, 4, 7, 1) + .right(4) + .box(fg, 1, 8, 1) + .right() + .box(fg, 1, 9, 1) + .right().up() + .box(fg, 3, 10, 1) + .right(3).up() + .box(fg, 2, 9, 1) + .right(2).up() + .box(fg, 2, 8, 1) + .right(2).up() + .box(fg, 1, 7, 1) + .right().up() + .box(fg, 1, 6, 1) + .right().up() + .box(fg, 1, 5, 1) + .right().up(2) + .box(fg, 1, 3, 1) + .left(9).up(3) + .box(fg, 10, 31, 1) + + // Draw the S + // It's drawn in three strokes from bottom to top. Look for when + // it switches from .right() to .left() then back again + + // move to starting point for S + .right(22).down(6) + // stroke 1 + .box(fg) + .right().down() + .box(fg, 1, 3, 1) + .right().down() + .box(fg, 1, 5, 1) + .right().down() + .box(fg, 1, 7, 1) + .right() + .box(fg, 1, 8, 1) + .right().down() + .box(fg, 1, 10, 1) + .right() + .box(fg, 1, 9, 1) + .right() + .box(fg, 1, 8, 1) + .right().down() + .box(fg, 2, 8, 1) + .right(2) + .box(fg, 4, 7, 1) + .right(4) + .box(fg, 2, 8, 1) + .right(2) + .box(fg, 1, 9, 1) + .right().up() + .box(fg, 1, 9, 1) + .right() + .box(fg, 1, 10, 1) + .right() + .box(fg, 1, 22, 1) + .right().up() + .box(fg, 2, 20, 1) + .right().up() + .box(fg, 1, 18, 1) + .right().up() + .box(fg, 1, 17, 1) + .right().up() + .box(fg, 1, 15, 1) + .right().up() + .box(fg, 1, 13, 1) + .right().up(2) + .box(fg, 1, 9, 1) + .right().up(2) + .box(fg, 1, 5, 1) + // stroke 2 + .left(8).up(4) + .box(fg, 1, 9, 1) + .left().up() + .box(fg, 1, 9, 1) + .left().up() + .box(fg, 1, 8, 1) + .left(2).up() + .box(fg, 2, 8, 1) + .left(2).up() + .box(fg, 2, 7, 1) + .left(2).up() + .box(fg, 2, 7, 1) + .left() + .box(fg, 1, 8, 1) + .left().up() + .box(fg, 1, 8, 1) + .left() + .box(fg, 1, 9, 1) + .left(2).up() + .box(fg, 2, 19, 1) + .left().up() + .box(fg, 1, 17, 1) + .left() + .box(fg, 1, 16, 1) + .left().up() + .box(fg, 1, 14, 1) + .left().up(2) + .box(fg, 1, 10, 1) + .left().up(2) + .box(fg, 1, 6, 1) + // stroke 3 + .right(7).up(6) + .box(fg, 1, 8, 1) + .right().up() + .box(fg, 1, 7, 1) + .right().up() + .box(fg, 2, 7, 1) + .right(2).up() + .box(fg, 4, 6, 1) + .right(4).down() + .box(fg, 2, 7, 1) + .right().down() + .box(fg, 1, 8, 1) + .right() + .box(fg, 1, 7, 1) + .right().down() + .box(fg, 1, 8, 1) + .right().down() + .box(fg, 1, 9, 1) + .right().down() + .box(fg, 1, 9, 1) + .right().up() + .box(fg, 1, 8, 1) + .right().up() + .box(fg, 1, 6, 1) + .right().up() + .box(fg, 1, 5, 1) + .right().up() + .box(fg, 1, 3, 1) + .right().up() + .box(fg); + + this.move('logojs-start'); + + return this; +}); +// +// Makes a cube of JS logos! +// This is a wrapper for logojs() so look at its docs +// +// Until the drone can rotate on its Z axis we can't +// use logojs() to create top/bottom sides of cube. +// +Drone.extend('logojscube', function(fg, bg) { + + this.chkpt('jscube-start') + .logojs(fg, bg); + + this.move('jscube-start') + .right(100) + .turn(3) + .logojs(fg, bg); + + this.move('jscube-start') + .right(100) + .turn(3) + .right(100) + .turn(3) + .logojs(fg, bg); + + this.move('jscube-start') + .right(100) + .turn(3) + .right(100) + .turn(3) + .right(100) + .turn(3) + .logojs(fg, bg); + + return this; +}); diff --git a/src/main/javascript/plugins/drone/contrib/rainbow.js b/src/main/javascript/plugins/drone/contrib/rainbow.js new file mode 100644 index 0000000..870cbc5 --- /dev/null +++ b/src/main/javascript/plugins/drone/contrib/rainbow.js @@ -0,0 +1,44 @@ +var Drone = require('../drone'); +var blocks = require('../blocks'); +module.exports = Drone; +/************************************************************************ +Drone.rainbow() method +====================== +Creates a Rainbow. + +Parameters +---------- + + * radius (optional - default:18) - The radius of the rainbow + +Example +------- + + var d = new Drone(); + d.rainbow(30); + +![rainbow example](img/rainbowex1.png) + +***/ +Drone.extend('rainbow', function(radius){ + if (typeof radius == "undefined") + radius = 18; + + this.chkpt('rainbow'); + this.down(radius); + // copy blocks.rainbow and add air at end (to compensate for strokewidth) + var colors = blocks.rainbow.slice(0); + colors.push(blocks.air); + for (var i = 0;i < colors.length; i++) { + var bm = this._getBlockIdAndMeta(colors[i]); + this.arc({ + blockType: bm[0], + meta: bm[1], + radius: radius-i, + strokeWidth: 2, + quadrants: {topright: true, + topleft: true}, + orientation: 'vertical'}).right().up(); + } + return this.move('rainbow'); +}); diff --git a/src/main/javascript/plugins/drone/contrib/rboxcall.js b/src/main/javascript/plugins/drone/contrib/rboxcall.js new file mode 100644 index 0000000..4546eec --- /dev/null +++ b/src/main/javascript/plugins/drone/contrib/rboxcall.js @@ -0,0 +1,34 @@ +var Drone = require('../drone'); +module.exports = Drone; +/** +* Iterates over each cube in a cubic region. For each cube has a chance to callback your +* function and provide a new drone to it. +* +* Parameters: +* callback - any function that accepts a drone as its first argument +* probability - chance to invoke your callback on each iteration +* width - width of the region +* height - (Optional) height of the region, defaults to width +* depth - (Optional) depth of the cube, defaults to width +*/ + +Drone.extend("rboxcall", function(callback, probability, width, height, depth) { + this.chkpt('rboxcall-start'); + + for(var i = 0; i < width; ++i) { + this.move('rboxcall-start').right(i); + for(var j = 0; j < depth; ++j) { + this.move('rboxcall-start').right(i).fwd(j); + for(var k = 0; k < height; ++k) { + if(Math.random()*100 < probability) { + callback.call(null, new Drone(this.x, this.y, this.z)); + } + this.up(); + } + } + } + + this.move('rboxcall-start'); + + return this; +}); diff --git a/src/main/javascript/plugins/drone/contrib/redstonewire.js b/src/main/javascript/plugins/drone/contrib/redstonewire.js new file mode 100644 index 0000000..97dc0f8 --- /dev/null +++ b/src/main/javascript/plugins/drone/contrib/redstonewire.js @@ -0,0 +1,108 @@ +var Drone = require('../drone'); +var blocks = require('../blocks'); +module.exports = Drone; +// +// usage: +// [1] to place a new block with redstone wire on it (block on bottom, redstone on top) +// /js wireblock(blocks.sandstone); +// +// [2] to drop wire on to an existing block +// /js wire() +// +// [3] to place a (redstone) torch on a new block +// /js torchblock(blocks.sandstone) +// +// [4] to place a repeater on a new block +// /js repeaterblock(blocks.sandstone) +// +// [5] To create a long redstone wire (with necessary repeaters, powererd by a single torch) +// /js wirestraight(blocks.sandstone, distance) +// +// [6] To create a 'road' with redstone torches and wire lining each side +// /js redstoneroad(blocks.stone, blocks.sandstone, 25) + +Drone.extend('wireblock',function(blockType) +{ + this.chkpt('wireblock') + .box(blockType,1,2,1) // 2 blocks tall, top block will be wire dropped on lower + .up(); + + this.world.getBlockAt(this.x,this.y,this.z).setTypeId(55); //apply wire + + return this.move('wireblock'); +}); + +Drone.extend('wire',function () +{ + this.chkpt('wire') + .up(); + + this.world.getBlockAt(this.x,this.y,this.z).setTypeId(55); // apply wire + + return this.move('wire'); +}); + +Drone.extend('torchblock', function(blockType) +{ + this.box(blockType,1,2,1) // 2 blocks tall + .up(); + + this.world.getBlockAt(this.x,this.y,this.z).setTypeId(76); // apply torch + + return this.down(); +}); + +Drone.extend('repeaterblock',function(blockType) +{ + this.chkpt('repeaterblock') + .box(blockType,1,2,1) + .up(); + + var block = this.world.getBlockAt(this.x,this.y,this.z); + block.setTypeId(94); // apply repeater + + // redstone repeater dirs: north=0,east=1,south=2,west=3 + var direction = [1,2,3,0][this.dir]; // convert drone dir to repeater dir. + block.setData(direction); + + return this.move('repeaterblock'); +}); + + +Drone.extend('wirestraight',function (blockType,distance) +{ + this.chkpt('wirestraight'); + + this.torchblock(blockType); + this.fwd(); + + for (var i = 1; i < distance; i++) { + if(i % 14 == 0) + { + this.repeaterblock(blockType); + } + else + { + this.wireblock(blockType); + } + + this.fwd(); + }; + + return this.move('wirestraight'); +}); + + +Drone.extend('redstoneroad', function (roadBlockType, redstoneunderBlockType, distance) +{ + return this.down() + .wirestraight(redstoneunderBlockType, distance) + .right() + .box(roadBlockType, 4,1,distance) + .right(4) + .wirestraight(redstoneunderBlockType, distance) + .up(); +}); + + + diff --git a/src/main/javascript/plugins/drone/contrib/skyscraper-example.js b/src/main/javascript/plugins/drone/contrib/skyscraper-example.js new file mode 100644 index 0000000..13190dc --- /dev/null +++ b/src/main/javascript/plugins/drone/contrib/skyscraper-example.js @@ -0,0 +1,19 @@ +var Drone = require('../drone'); +var blocks = require('../blocks'); + +module.exports = Drone; +Drone.extend('skyscraper',function(floors){ + + if (typeof floors == "undefined") + floors = 10; + this.chkpt('skyscraper'); + for (var i = 0;i < floors; i++) + { + this + .box(blocks.iron,20,1,20) + .up() + .box0(blocks.glass_pane,20,3,20) + .up(3); + } + return this.move('skyscraper'); +}); diff --git a/src/main/javascript/plugins/drone/contrib/spiral_stairs.js b/src/main/javascript/plugins/drone/contrib/spiral_stairs.js new file mode 100644 index 0000000..39b818f --- /dev/null +++ b/src/main/javascript/plugins/drone/contrib/spiral_stairs.js @@ -0,0 +1,48 @@ +var Drone = require('../drone'); +var blocks = require('../blocks'); + +module.exports = Drone; +/************************************************************************ +Drone.spiral_stairs() method +============================ +Constructs a spiral staircase with slabs at each corner. + +Parameters +---------- + + * stairBlock - The block to use for stairs, should be one of the following... + - 'oak' + - 'spruce' + - 'birch' + - 'jungle' + - 'cobblestone' + - 'brick' + - 'stone' + - 'nether' + - 'sandstone' + - 'quartz' + * flights - The number of flights of stairs to build. + +![Spiral Staircase](img/spiralstair1.png) + +Example +------- +To construct a spiral staircase 5 floors high made of oak... + + spiral_stairs('oak', 5); + +***/ +Drone.extend("spiral_stairs",function(stairBlock, flights){ + this.chkpt('spiral_stairs'); + + for (var i = 0; i < flights; i++){ + this + .box(blocks.stairs[stairBlock] + ':' + Drone.PLAYER_STAIRS_FACING[this.dir]) + .up().fwd() + .box(blocks.stairs[stairBlock] + ':' + Drone.PLAYER_STAIRS_FACING[this.dir]) + .up().fwd() + .box(blocks.slab[stairBlock]) + .turn().fwd(); + } + return this.move('spiral_stairs'); +}); diff --git a/src/main/javascript/plugins/drone/contrib/streamer.js b/src/main/javascript/plugins/drone/contrib/streamer.js new file mode 100644 index 0000000..9c2031e --- /dev/null +++ b/src/main/javascript/plugins/drone/contrib/streamer.js @@ -0,0 +1,32 @@ +var Drone = require('../drone'); +module.exports = Drone; +/** +* Creates a stream of blocks in a given direction until it hits something other than air +* +* Parameters: +* block - blockId +* dir - "up", "down", "left", "right", "fwd", "back +* maxIterations - (Optional) maximum number of cubes to generate, defaults to 1000 +*/ +Drone.extend("streamer", function(block, dir, maxIterations) { + if (typeof maxIterations == "undefined") + maxIterations = 1000; + + var usage = "Usage: streamer({block-type}, {direction: 'up', 'down', 'fwd', 'back', 'left', 'right'}, {maximum-iterations: default 1000})\nE.g.\n" + + "streamer(5, 'up', 200)"; + if (typeof dir == "undefined"){ + throw new Error(usage); + } + if (typeof block == "undefined") { + throw new Error(usage); + } + for ( var i = 0; i < maxIterations||1000; ++i ) { + this.box(block); + this[dir].call(this); + var block = this.world.getBlockAt(this.x, this.y, this.z); + if ( block.typeId != 0 && block.data != 0) { + break + } + } + return this; +}); diff --git a/src/main/javascript/plugins/drone/contrib/temple.js b/src/main/javascript/plugins/drone/contrib/temple.js new file mode 100644 index 0000000..c774a9b --- /dev/null +++ b/src/main/javascript/plugins/drone/contrib/temple.js @@ -0,0 +1,25 @@ +var Drone = require('../drone'); +module.exports = Drone; +// +// constructs a mayan temple +// +Drone.extend('temple', function(side) { + if (!side) { + side = 20; + } + var stone = '98:1'; + var stair = '109:' + Drone.PLAYER_STAIRS_FACING[this.dir]; + + this.chkpt('temple'); + + while (side > 4) { + var middle = Math.round((side-2)/2); + this.chkpt('corner') + .box(stone, side, 1, side) + .right(middle).box(stair).right().box(stair) + .move('corner').up().fwd().right(); + side = side - 2; + } + + return this.move('temple'); +}); diff --git a/src/main/javascript/plugins/drone/drone-exts.js b/src/main/javascript/plugins/drone/drone-exts.js new file mode 100644 index 0000000..08da5da --- /dev/null +++ b/src/main/javascript/plugins/drone/drone-exts.js @@ -0,0 +1,25 @@ +var Drone = require('./drone'); +var utils = require('../utils/utils'); + +var files = []; + +var filter = function(file,name){ + name = "" + name; + if (name.match(/drone\.js$/)) + return false; + if (name.match(/drone\-exts\.js$/)) + return false; + if (name.match(/\.js$/)) + return true; + if (file.isDirectory()) + return true; + return false; +}; + +var files = utils.find(__dirname, filter); + +utils.foreach(files, function (file){ + require(file); +}); + +module.exports = Drone; diff --git a/src/main/javascript/plugins/drone/drone-firework.js b/src/main/javascript/plugins/drone/drone-firework.js new file mode 100644 index 0000000..ac07f37 --- /dev/null +++ b/src/main/javascript/plugins/drone/drone-firework.js @@ -0,0 +1,6 @@ +var fireworks = require('fireworks'); +var Drone = require('./drone').Drone; +Drone.extend('firework',function() { + fireworks.firework(this.getLocation()); +}); + diff --git a/src/main/javascript/plugins/drone/drone.js b/src/main/javascript/plugins/drone/drone.js new file mode 100644 index 0000000..4686c9d --- /dev/null +++ b/src/main/javascript/plugins/drone/drone.js @@ -0,0 +1,1740 @@ +var _utils = require('../utils/utils'); +var blocks = require('./blocks'); + +/********************************************************************* +Drone Module +============ +The Drone is a convenience class for building. It can be used for... + + 1. Building + 2. Copying and Pasting + +It uses a fluent interface which means all of the Drone's methods return `this` and can +be chained together like so... + + var theDrone = new Drone(); + theDrone.up().left().box(blocks.oak).down().fwd(3).cylinder0(blocks.lava,8); + +TLDNR; (Just read this if you're impatient) +=========================================== +At the in-game command prompt type... + + /js box( blocks.oak ) + +... creates a single wooden block at the cross-hairs or player location + + /js box( blocks.oak ).right(2).box( blocks.wool.black, 4, 9, 1) + +... creates a single wooden block and a 2001 black obelisk that is 4 +wide x 9 tall x 1 long in size. If you want to see what else +ScriptCraft's Drone can do, read on... + +Constructing a Drone Object +=========================== + +Drones can be created in any of the following ways... + + 1. Calling any one of the methods listed below will return a Drone object. For example... + + var d = box( blocks.oak ) + + ... creates a 1x1x1 wooden block at the cross-hairs or player's location and returns a Drone + object. This might look odd (if you're familiar with Java's Object-dot-method syntax) but all + of the Drone class's methods are also global functions that return new Drone objects. + This is short-hand for creating drones and is useful for playing around with Drones at the in-game + command prompt. It's shorter than typing ... + + var d = new Drone().box( blocks.oak ) + + ... All of the Drone's methods return `this` (self) so you can chain operations together like this... + + var d = box( blocks.oak ) + .up() + .box( blocks.oak ,3,1,3) + .down() + .fwd(2) + .box( blocks.oak ) + .turn() + .fwd(2) + .box( blocks.oak ) + .turn() + .fwd(2) + .box( blocks.oak ); + + 2. Using the following form... + + d = new Drone() + + ...will create a new Drone. If the cross-hairs are pointing at a + block at the time then, that block's location becomes the drone's + starting point. If the cross-hairs are _not_ pointing at a block, + then the drone's starting location will be 2 blocks directly in + front of the player. TIP: Building always happens right and front + of the drone's position... + + Plan View: + + ^ + | + | + D----> + + For convenience you can use a _corner stone_ to begin building. + The corner stone should be located just above ground level. If + the cross-hair is point at or into ground level when you create a + new Drone(), then building begins at that point. You can get + around this by pointing at a 'corner stone' just above ground + level or alternatively use the following statement... + + d = new Drone().up(); + + ... which will move the drone up one block as soon as it's created. + + ![corner stone](img/cornerstone1.png) + + 3. Or by using the following form... + + d = new Drone(x,y,z,direction,world); + + This will create a new Drone at the location you specified using + x, y, z In minecraft, the X axis runs west to east and the Z axis runs + north to south. The direction parameter says what direction you want + the drone to face: 0 = east, 1 = south, 2 = west, 3 = north. If the + direction parameter is omitted, the player's direction is used + instead. + + Both the `direction` and `world` parameters are optional. + + 4. Create a new Drone based on a Bukkit Location object... + + d = new Drone(location); + + This is useful when you want to create a drone at a given + `org.bukkit.Location` . The `Location` class is used throughout + the bukkit API. For example, if you want to create a drone when a + block is broken at the block's location you would do so like + this... + + events.on('block.BlockBreakEvent',function(listener,event){ + var location = event.block.location; + var drone = new Drone(location); + // do more stuff with the drone here... + }); + +Parameters +---------- + * location (optional) : *NB* If an `org.bukkit.Location` object is provided as a parameter, then it should be the only parameter. + * x (optional) : The x coordinate of the Drone + * y (optional) : The y coordinate of the Drone + * z (optional) : The z coordinate of the Drone + * direction (optional) : The direction in which the Drone is + facing. Possible values are 0 (east), 1 (south), 2 (west) or 3 (north) + * world (optional) : The world in which the drone is created. + +Drone.box() method +================== +the box() method is a convenience method for building things. (For the more performance-oriented method - see cuboid) + +parameters +---------- + * b - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling. + Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch` + * w (optional - default 1) - the width of the structure + * h (optional - default 1) - the height of the structure + * d (optional - default 1) - the depth of the structure - NB this is + not how deep underground the structure lies - this is how far + away (depth of field) from the drone the structure will extend. + +Example +------- +To create a black structure 4 blocks wide, 9 blocks tall and 1 block long... + + box(blocks.wool.black, 4, 9, 1); + +... or the following code does the same but creates a variable that can be used for further methods... + + var drone = new Drone(); + drone.box(blocks.wool.black, 4, 9, 1); + +![box example 1](img/boxex1.png) + +Drone.box0() method +=================== +Another convenience method - this one creates 4 walls with no floor or ceiling. + +Parameters +---------- + * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling. + Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch` + * width (optional - default 1) - the width of the structure + * height (optional - default 1) - the height of the structure + * length (optional - default 1) - the length of the structure - how far + away (depth of field) from the drone the structure will extend. + +Example +------- +To create a stone building with the insided hollowed out 7 wide by 3 tall by 6 long... + + box0( blocks.stone, 7, 3, 6); + +![example box0](img/box0ex1.png) + +Drone.boxa() method +=================== +Construct a cuboid using an array of blocks. As the drone moves first along the width axis, +then the height (y axis) then the length, each block is picked from the array and placed. + +Parameters +---------- + * blocks - An array of blocks - each block in the array will be placed in turn. + * width + * height + * length + +Example +------- +Construct a rainbow-colored road 100 blocks long... + + var rainbowColors = [blocks.wool.red, blocks.wool.orange, blocks.wool.yellow, blocks.wool.lime, + blocks.wool.lightblue, blocks.wool.blue, blocks.wool.purple]; + + boxa(rainbowColors,7,1,30); + +![boxa example](img/boxaex1.png) + +Drone Movement +============== +Drones can move freely in minecraft's 3-D world. You control the +Drone's movement using any of the following methods.. + + * up() + * down() + * left() + * right() + * fwd() + * back() + * turn() + +... Each of these methods takes a single optional parameter +`numBlocks` - the number of blocks to move in the given direction. If +no parameter is given, the default is 1. + +to change direction use the `turn()` method which also takes a single +optional parameter (numTurns) - the number of 90 degree turns to make. +Turns are always clock-wise. If the drone is facing north, then +drone.turn() will make the turn face east. If the drone is facing east +then drone.turn(2) will make the drone turn twice so that it is facing +west. + +Drone Positional Info +===================== + + * getLocation() - Returns a Bukkit Location object for the drone + +Drone Markers +============= +Markers are useful when your Drone has to do a lot of work. You can +set a check-point and return to the check-point using the move() +method. If your drone is about to undertake a lot of work - +e.g. building a road, skyscraper or forest you should set a +check-point before doing so if you want your drone to return to its +current location. + +A 'start' checkpoint is automatically created when the Drone is first created. + +Markers are created and returned to using the followng two methods... + + * chkpt - Saves the drone's current location so it can be returned to later. + * move - moves the drone to a saved location. Alternatively you can provide an + org.bukkit.Location object or x,y,z and direction parameters. + +Parameters +---------- + * name - the name of the checkpoint to save or return to. + +Example +------- + + drone.chkpt('town-square'); + // + // the drone can now go off on a long excursion + // + for (i = 0; i< 100; i++){ + drone.fwd(12).box(6); + } + // + // return to the point before the excursion + // + drone.move('town-square'); + +Drone.prism() method +==================== +Creates a prism. This is useful for roofs on houses. + +Parameters +---------- + + * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling. + Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch` + * width - the width of the prism + * length - the length of the prism (will be 2 time its height) + +Example +------- + + prism(blocks.oak,3,12); + +![prism example](img/prismex1.png) + +Drone.prism0() method +===================== +A variation on `prism` which hollows out the inside of the prism. It uses the same parameters as `prism`. + +Drone.cylinder() method +======================= +A convenience method for building cylinders. Building begins radius blocks to the right and forward. + +Parameters +---------- + + * block - the block id - e.g. 6 for an oak sapling or '6:2' for a birch sapling. + Alternatively you can use any one of the `blocks` values e.g. `blocks.sapling.birch` + * radius + * height + +Example +------- +To create a cylinder of Iron 7 blocks in radius and 1 block high... + + cylinder(blocks.iron, 7 , 1); + +![cylinder example](img/cylinderex1.png) + +Drone.cylinder0() method +======================== +A version of cylinder that hollows out the middle. + +Example +------- +To create a hollow cylinder of Iron 7 blocks in radius and 1 block high... + + cylinder0(blocks.iron, 7, 1); + +![cylinder0 example](img/cylinder0ex1.png) + +Drone.arc() method +================== +The arc() method can be used to create 1 or more 90 degree arcs in the horizontal or vertical planes. +This method is called by cylinder() and cylinder0() and the sphere() and sphere0() methods. + +Parameters +---------- +arc() takes a single parameter - an object with the following named properties... + + * radius - The radius of the arc. + * blockType - The type of block to use - this is the block Id only (no meta). See [Data Values][dv]. + * meta - The metadata value. See [Data Values][dv]. + * orientation (default: 'horizontal') - the orientation of the arc - can be 'vertical' or 'horizontal'. + * stack (default: 1) - the height or length of the arc (depending on + the orientation - if orientation is horizontal then this parameter + refers to the height, if vertical then it refers to the length). + * strokeWidth (default: 1) - the width of the stroke (how many + blocks) - if drawing nested arcs it's usually a good idea to set + strokeWidth to at least 2 so that there are no gaps between each + arc. The arc method uses a [bresenham algorithm][bres] to plot + points along the circumference. + * fill - If true (or present) then the arc will be filled in. + * quadrants (default: + `{topleft:true,topright:true,bottomleft:true,bottomright:true}` - An + object with 4 properties indicating which of the 4 quadrants of a + circle to draw. If the quadrants property is absent then all 4 + quadrants are drawn. + +Examples +-------- +To draw a 1/4 circle (top right quadrant only) with a radius of 10 and stroke width of 2 blocks ... + + arc({blockType: blocks.iron, + meta: 0, + radius: 10, + strokeWidth: 2, + quadrants: { topright: true }, + orientation: 'vertical', + stack: 1, + fill: false + }); + +![arc example 1](img/arcex1.png) + +[bres]: http://en.wikipedia.org/wiki/Midpoint_circle_algorithm +[dv]: http://www.minecraftwiki.net/wiki/Data_values + +Drone.door() method +=================== +create a door - if a parameter is supplied an Iron door is created otherwise a wooden door is created. + +Parameters +---------- + * doorType (optional - default wood) - If a parameter is provided then the door is Iron. + +Example +------- +To create a wooden door at the crosshairs/drone's location... + + var drone = new Drone(); + drone.door(); + +To create an iron door... + + drone.door( blocks.door_iron ); + +![iron door](img/doorex1.png) + +Drone.door2() method +==================== +Create double doors (left and right side) + +Parameters +---------- + * doorType (optional - default wood) - If a parameter is provided then the door is Iron. + +Example +------- +To create double-doors at the cross-hairs/drone's location... + + drone.door2(); + +![double doors](img/door2ex1.png) + +Drone.sign() method +=================== +Signs must use block 63 (stand-alone signs) or 68 (signs on walls) + +Parameters +---------- + * message - can be a string or an array of strings. + * block - can be 63 or 68 + +Example +------- +To create a free-standing sign... + + drone.sign(["Hello","World"],63); + +![ground sign](img/signex1.png) + +... to create a wall mounted sign... + + drone.sign(["Welcome","to","Scriptopia"], 68); + +![wall sign](img/signex2.png) + +Drone Trees methods +=================== + + * oak() + * spruce() + * birch() + * jungle() + +Example +------- +To create 4 trees in a row, point the cross-hairs at the ground then type `/js ` and ... + + up().oak().right(8).spruce().right(8).birch().right(8).jungle(); + +Trees won't always generate unless the conditions are right. You +should use the tree methods when the drone is directly above the +ground. Trees will usually grow if the drone's current location is +occupied by Air and is directly above an area of grass (That is why +the `up()` method is called first). + +![tree example](img/treeex1.png) + + +None of the tree methods require parameters. Tree methods will only be successful +if the tree is placed on grass in a setting where trees can grow. + +Drone.garden() method +===================== +places random flowers and long grass (similar to the effect of placing bonemeal on grass) + +Parameters +---------- + + * width - the width of the garden + * length - how far from the drone the garden extends + +Example +------- +To create a garden 10 blocks wide by 5 blocks long... + + garden(10,5); + +![garden example](img/gardenex1.png) + +Drone.rand() method +=================== +rand takes either an array (if each blockid has the same chance of occurring) +or an object where each property is a blockid and the value is it's weight (an integer) + +Example +------- +place random blocks stone, mossy stone and cracked stone (each block has the same chance of being picked) + + rand( [blocks.brick.stone, blocks.brick.mossy, blocks.brick.cracked ],w,d,h) + +to place random blocks stone has a 50% chance of being picked, + + rand({blocks.brick.stone: 5, blocks.brick.mossy: 3, blocks.brick.cracked: 2},w,d,h) + +regular stone has a 50% chance, mossy stone has a 30% chance and cracked stone has just a 20% chance of being picked. + +Copy & Paste using Drone +======================== +A drone can be used to copy and paste areas of the game world. + +Drone.copy() method +=================== +Copies an area so it can be pasted elsewhere. The name can be used for +pasting the copied area elsewhere... + +Parameters +---------- + + * name - the name to be given to the copied area (used by `paste`) + * width - the width of the area to copy + * height - the height of the area to copy + * length - the length of the area (extending away from the drone) to copy + +Example +------- + + drone.copy('somethingCool',10,5,10).right(12).paste('somethingCool'); + +Drone.paste() method +==================== +Pastes a copied area to the current location. + +Example +------- +To copy a 10x5x10 area (using the drone's coordinates as the starting +point) into memory. the copied area can be referenced using the name +'somethingCool'. The drone moves 12 blocks right then pastes the copy. + + drone.copy('somethingCool',10,5,10) + .right(12) + .paste('somethingCool'); + +Chaining +======== + +All of the Drone methods return a Drone object, which means methods +can be 'chained' together so instead of writing this... + + drone = new Drone(); + drone.fwd(3); + drone.left(2); + drone.box(2); // create a grass block + drone.up(); + drone.box(2); // create another grass block + drone.down(); + +...you could simply write ... + + var drone = new Drone().fwd(3).left(2).box(2).up().box(2).down(); + +... since each Drone method is also a global function that constructs +a drone if none is supplied, you can shorten even further to just... + + fwd(3).left(2).box(2).up().box(2).down() + +The Drone object uses a [Fluent Interface][fl] to make ScriptCraft +scripts more concise and easier to write and read. Minecraft's +in-game command prompt is limited to about 80 characters so chaining +drone commands together means more can be done before hitting the +command prompt limit. For complex building you should save your +commands in a new script file and load it using /js load() + +[fl]: http://en.wikipedia.org/wiki/Fluent_interface + +Drone Properties +================ + + * x - The Drone's position along the west-east axis (x increases as you move east) + * y - The Drone's position along the vertical axis (y increses as you move up) + * z - The Drone's position along the north-south axis (z increases as you move south) + * dir - The Drone's direction 0 is east, 1 is south , 2 is west and 3 is north. + +Extending Drone +=============== +The Drone object can be easily extended - new buidling recipes/blue-prints can be added and can +become part of a Drone's chain using the *static* method `Drone.extend`. + +Drone.extend() static method +============================ +Use this method to add new methods (which also become chainable global functions) to the Drone object. + +Parameters +---------- + * name - The name of the new method e.g. 'pyramid' + * function - The method body. + +Example +------- + + // submitted by [edonaldson][edonaldson] + Drone.extend('pyramid', function(block,height){ + this.chkpt('pyramid'); + for (var i = height; i > 0; i -= 2) { + this.box(block, i, 1, i).up().right().fwd(); + } + return this.move('pyramid'); + }); + +Once the method is defined (it can be defined in a new pyramid.js file) it can be used like so... + + var d = new Drone(); + d.pyramid(blocks.brick.stone, 12); + +... or simply ... + + pyramid(blocks.brick.stone, 12); + +[edonaldson]: https://github.com/edonaldson + +Drone Constants +=============== + +Drone.PLAYER_STAIRS_FACING +-------------------------- +An array which can be used when constructing stairs facing in the Drone's direction... + + var d = new Drone(); + d.box(blocks.stairs.oak + ':' + Drone.PLAYER_STAIRS_FACING[d.dir]); + +... will construct a single oak stair block facing the drone. + +Drone.PLAYER_SIGN_FACING +------------------------ +An array which can be used when placing signs so they face in a given direction. +This is used internally by the Drone.sign() method. It should also be used for placing +any of the following blocks... + + * chest + * ladder + * furnace + * dispenser + +To place a chest facing the Drone ... + + drone.box( blocks.chest + ':' + Drone.PLAYER_SIGN_FACING[drone.dir]); + +Drone.PLAYER_TORCH_FACING +------------------------- +Used when placing torches so that they face towards the drone. + + drone.box( blocks.torch + ':' + Drone.PLAYER_TORCH_FACING[drone.dir]); + +***/ + +// +// Implementation +// ============== +// +// There is no need to read any further unless you want to understand how the Drone object works. +// + +var putBlock = function(x,y,z,blockId,metadata,world){ + if (typeof metadata == "undefined") + metadata = 0; + var block = world.getBlockAt(x,y,z); + if (block.typeId != blockId || block.data != metadata) + block.setTypeIdAndData(blockId,metadata,false); +}; + +var putSign = function(texts, x, y, z, blockId, meta, world){ + if (blockId != 63 && blockId != 68) + throw new Error("Invalid Parameter: blockId must be 63 or 68"); + putBlock(x,y,z,blockId,meta,world); + var block = world.getBlockAt(x,y,z); + var state = block.state; + if (state instanceof org.bukkit.block.Sign){ + for (var i = 0;i < texts.length; i++) + state.setLine(i%4,texts[i]); + state.update(true); + } +}; + +Drone = function(x,y,z,dir,world) +{ + this.record = false; + var usePlayerCoords = false; + var playerPos = _utils.getPlayerPos(); + if (typeof x == "undefined") + { + var mp = _utils.getMousePos(); + if (mp){ + this.x = mp.x; + this.y = mp.y; + this.z = mp.z; + if (playerPos) + this.dir = _getDirFromRotation(playerPos.yaw); + this.world = mp.world; + }else{ + // base it on the player's current location + usePlayerCoords = true; + // + // it's possible that drone.js could be loaded by a non-playing op + // (from the server console) + // + if (!playerPos){ + return null; + } + this.x = playerPos.x; + this.y = playerPos.y; + this.z = playerPos.z; + this.dir = _getDirFromRotation(playerPos.yaw); + this.world = playerPos.world; + } + }else{ + if (arguments[0] instanceof org.bukkit.Location){ + this.x = arguments[0].x; + this.y = arguments[0].y; + this.z = arguments[0].z; + this.dir = _getDirFromRotation(arguments[0].yaw); + this.world = arguments[0].world; + }else{ + this.x = x; + this.y = y; + this.z = z; + if (typeof dir == "undefined"){ + this.dir = _getDirFromRotation(playerPos.yaw); + }else{ + this.dir = dir%4; + } + if (typeof world == "undefined"){ + this.world = _getWorld(); + }else{ + this.world = world; + } + } + } + + if (usePlayerCoords){ + this.fwd(3); + } + this.chkpt('start'); + this.record = true; + this.history = []; + // for debugging + // self.sendMessage("New Drone " + this.toString()); + return this; +}; + +module.exports = Drone; + +// +// add custom methods to the Drone object using this function +// +Drone.extend = function(name, func) +{ + Drone.prototype['_' + name] = func; + Drone.prototype[name] = function(){ + if (this.record) + this.history.push([name,arguments]); + var oldVal = this.record; + this.record = false; + this['_' + name].apply(this,arguments); + this.record = oldVal; + return this; + }; + + global[name] = function(){ + var result = new Drone(); + result[name].apply(result,arguments); + return result; + }; +}; + +/************************************************************************** +Drone.times() Method +==================== +The times() method makes building multiple copies of buildings easy. It's possible to create rows or grids of buildings without resorting to `for` or `while` loops. + +Parameters +---------- + * numTimes (optional - default 2) : The number of times you want to repeat the preceding statements. + +Example +------- +Say you want to do the same thing over and over. You have a couple of options... + + * You can use a for loop... + + d = new Drone(); for (var i =0;i < 4; i++){ d.cottage().right(8); } + +While this will fit on the in-game prompt, it's awkward. You need to +declare a new Drone object first, then write a for loop to create the +4 cottages. It's also error prone, even the `for` loop is too much +syntax for what should really be simple. + + * You can use a while loop... + + d = new Drone(); var i=4; while (i--){ d.cottage().right(8); } + +... which is slightly shorter but still too much syntax. Each of the +above statements is fine for creating a 1-dimensional array of +structures. But what if you want to create a 2-dimensional or +3-dimensional array of structures? Enter the `times()` method. + +The `times()` method lets you repeat commands in a chain any number of +times. So to create 4 cottages in a row you would use the following +statement... + + cottage().right(8).times(4); + +...which will build a cottage, then move right 8 blocks, then do it +again 4 times over so that at the end you will have 4 cottages in a +row. What's more the `times()` method can be called more than once in +a chain. So if you wanted to create a *grid* of 20 houses ( 4 x 5 ), +you would do so using the following statement... + + cottage().right(8).times(4).fwd(8).left(32).times(5); + +... breaking it down... + + 1. The first 3 calls in the chain ( `cottage()`, `right(8)`, + `times(4)` ) build a single row of 4 cottages. + + 2. The last 3 calls in the chain ( `fwd(8)`, `left(32)`, `times(5)` ) + move the drone forward 8 then left 32 blocks (4 x 8) to return to + the original x coordinate, then everything in the chain is + repeated again 5 times so that in the end, we have a grid of 20 + cottages, 4 x 5. Normally this would require a nested loop but + the `times()` method does away with the need for loops when + repeating builds. + +Another example: This statement creates a row of trees 2 by 3 ... + + oak().right(10).times(2).left(20).fwd(10).times(3) + +... You can see the results below. + +![times example 1](img/times-trees.png) + +***/ +Drone.prototype.times = function(numTimes,commands) { + if (typeof numTimes == "undefined") + numTimes = 2; + if (typeof commands == "undefined") + commands = this.history.concat(); + + this.history = [['times',[numTimes+1,commands]]]; + var oldVal = this.record; + this.record = false; + for (var j = 1; j < numTimes; j++) + { + for (var i = 0;i < commands.length; i++){ + var command = commands[i]; + var methodName = command[0]; + var args = command[1]; + print ("command=" + JSON.stringify(command) + ",methodName=" + methodName); + this[methodName].apply(this,args); + } + } + this.record = oldVal; + return this; +}; + +Drone.prototype._checkpoints = {}; + +Drone.extend('chkpt',function(name){ + this._checkpoints[name] = {x:this.x,y:this.y,z:this.z,dir:this.dir}; +}); + +Drone.extend('move', function() { + if (arguments[0] instanceof org.bukkit.Location){ + this.x = arguments[0].x; + this.y = arguments[0].y; + this.z = arguments[0].z; + this.dir = _getDirFromRotation(arguments[0].yaw); + this.world = arguments[0].world; + }else if (typeof arguments[0] === "string"){ + var coords = this._checkpoints[arguments[0]]; + if (coords){ + this.x = coords.x; + this.y = coords.y; + this.z = coords.z; + this.dir = coords.dir%4; + } + }else{ + // expect x,y,z,dir + switch(arguments.length){ + case 4: + this.dir = arguments[3]; + case 3: + this.z = arguments[2]; + case 2: + this.y = arguments[1]; + case 1:n + this.x = arguments[0]; + } + } +}); + +Drone.extend('turn',function(n){ + if (typeof n == "undefined") + n = 1; + this.dir += n; + this.dir %=4; +}); +Drone.extend('right',function(n){ + if (typeof n == "undefined") + n = 1; + _movements[this.dir].right(this,n); +}); +Drone.extend('left',function(n){ + if (typeof n == "undefined") + n = 1; + _movements[this.dir].left(this,n); +}); +Drone.extend('fwd',function(n){ + if (typeof n == "undefined") + n = 1; + _movements[this.dir].fwd(this,n); +}); +Drone.extend('back',function(n){ + if (typeof n == "undefined") + n = 1; + _movements[this.dir].back(this,n); +}); +Drone.extend('up',function(n){ + if (typeof n == "undefined") + n = 1; + this.y+= n; +}); +Drone.extend('down',function(n){ + if (typeof n == "undefined") + n = 1; + this.y-= n; +}); +// +// position +// +Drone.prototype.getLocation = function() { + return new org.bukkit.Location(this.world, this.x, this.y, this.z); +}; +// +// building +// +Drone.extend('sign',function(message,block){ + if (message.constructor == Array){ + }else{ + message = [message]; + } + var bm = this._getBlockIdAndMeta(block); + block = bm[0]; + var meta = bm[1]; + if (block != 63 && block != 68){ + print("ERROR: Invalid block id for use in signs"); + return; + } + if (block == 68){ + meta = Drone.PLAYER_SIGN_FACING[this.dir%4]; + this.back(); + } + if (block == 63){ + meta = (12 + ((this.dir+2)*4)) % 16; + } + putSign(message,this.x,this.y,this.z,block,meta, this.world); + if (block == 68){ + this.fwd(); + } +}); +Drone.prototype.cuboida = function(/* Array */ blocks,w,h,d){ + var properBlocks = []; + var len = blocks.length; + for (var i = 0;i < len;i++){ + var bm = this._getBlockIdAndMeta(blocks[i]); + properBlocks.push([bm[0],bm[1]]); + } + if (typeof h == "undefined") + h = 1; + if (typeof d == "undefined") + d = 1; + if (typeof w == "undefined") + w = 1; + var that = this; + var dir = this.dir; + var pl = org.bukkit.entity.Player; + var cs = org.bukkit.command.BlockCommandSender; + var bi = 0; + /* + + */ + _traverse[dir].depth(that,d,function(){ + _traverseHeight(that,h,function(){ + _traverse[dir].width(that,w,function(){ + var block = that.world.getBlockAt(that.x,that.y,that.z); + var properBlock = properBlocks[bi%len]; + block.setTypeIdAndData(properBlock[0],properBlock[1],false); + bi++; + }); + }); + }); + return this; + +}; +/* + faster cuboid because blockid, meta and world must be provided + use this method when you need to repeatedly place blocks +*/ +Drone.prototype.cuboidX = function(blockType, meta, w, h, d){ + + if (typeof h == "undefined") + h = 1; + if (typeof d == "undefined") + d = 1; + if (typeof w == "undefined") + w = 1; + var that = this; + var dir = this.dir; + + var depthFunc = function(){ + var block = that.world.getBlockAt(that.x,that.y,that.z); + block.setTypeIdAndData(blockType,meta,false); + // wph 20130210 - dont' know if this is a bug in bukkit but for chests, + // the metadata is ignored (defaults to 2 - south facing) + // only way to change data is to set it using property/bean. + block.data = meta; + }; + var heightFunc = function(){ + _traverse[dir].depth(that,d,depthFunc); + }; + var widthFunc = function(){ + _traverseHeight(that,h,heightFunc); + }; + + _traverse[dir].width(that,w,widthFunc); + return this; + +}; + +Drone.prototype.cuboid = function(block,w,h,d){ + var bm = this._getBlockIdAndMeta(block); + return this.cuboidX(bm[0],bm[1], w,h,d); +}; +Drone.prototype.cuboid0 = function(block,w,h,d){ + this.chkpt('start_point'); + + // Front wall + this.cuboid(block, w, h, 1); + // Left wall + this.cuboid(block, 1, h, d); + // Right wall + this.right(w-1).cuboid(block, 1, h, d).left(w-1); + // Back wall + this.fwd(d-1).cuboid(block, w, h, 1); + + return this.move('start_point'); +}; +Drone.extend('door',function(door){ + if (typeof door == "undefined"){ + door = 64; + }else{ + door = 71; + } + this.cuboid(door+':' + this.dir).up().cuboid(door+':8').down(); +}); +Drone.extend('door2',function(door){ + if (typeof door == "undefined"){ + door = 64; + }else{ + door = 71; + } + this + .box(door+':' + this.dir).up() + .box(door+':8').right() + .box(door+':9').down() + .box(door+':' + this.dir).left(); +}); +// player dirs: 0 = east, 1 = south, 2 = west, 3 = north +// block dirs: 0 = east, 1 = west, 2 = south , 3 = north +// sign dirs: 5 = east, 3 = south, 4 = west, 2 = north +Drone.PLAYER_STAIRS_FACING = [0,2,1,3]; +// for blocks 68 (wall signs) 65 (ladders) 61,62 (furnaces) 23 (dispenser) and 54 (chest) +Drone.PLAYER_SIGN_FACING = [4,2,5,3]; +Drone.PLAYER_TORCH_FACING = [2,4,1,3]; + +var _getWorld = function(){ + var pl = org.bukkit.entity.Player; + var cs = org.bukkit.command.BlockCommandSender; + var world = (self instanceof pl)?self.location.world:(self instanceof cs)?self.block.location.world:null; + return world; +}; + +var _STAIRBLOCKS = {53: '5:0' // oak wood + ,67: 4 // cobblestone + ,108: 45 // brick + ,109: 98 // stone brick + ,114: 112 // nether brick + ,128: 24 // sandstone + ,134: '5:1' // spruce wood + ,135: '5:2' // birch wood + ,136: '5:3' // jungle wood + }; +// +// prism private implementation +// +var _prism = function(block,w,d) { + var stairEquiv = _STAIRBLOCKS[block]; + if (stairEquiv){ + this.fwd().prism(stairEquiv,w,d-2).back(); + var d2 = 0; + var middle = Math.floor(d/2); + var uc = 0,dc = 0; + while (d2 < d) + { + var di = (d2 < middle?this.dir:(this.dir+2)%4); + var bd = block + ':' + Drone.PLAYER_STAIRS_FACING[di]; + var putStep = true; + if (d2 == middle){ + if (d % 2 == 1){ + putStep = false; + } + } + if (putStep) + this.cuboid(bd,w); + if (d2 < middle-1){ + this.up(); + uc++; + } + var modulo = d % 2; + if (modulo == 1){ + if (d2 > middle && d2= middle && d2= 1){ + this.cuboid(block,w,1,d2); + d2 -= 2; + this.fwd().up(); + c++; + } + this.down(c).back(c); + } + return this; +}; +// +// prism0 private implementation +// +var _prism0 = function(block,w,d){ + this.prism(block,w,d) + .fwd().right() + .prism(0,w-2,d-2) + .left().back(); + var se = _STAIRBLOCKS[block]; + if (d % 2 == 1 && se){ + // top of roof will be open - need repair + var f = Math.floor(d/2); + this.fwd(f).up(f).cuboid(se,w).down(f).back(f); + } +}; +Drone.extend('prism0',_prism0); +Drone.extend('prism',_prism); +Drone.extend('box',Drone.prototype.cuboid); +Drone.extend('box0',Drone.prototype.cuboid0); +Drone.extend('boxa',Drone.prototype.cuboida); +// +// show the Drone's position and direction +// +Drone.prototype.toString = function(){ + var dirs = ["east","south","west","north"]; + return "x: " + this.x + " y: "+this.y + " z: " + this.z + " dir: " + this.dir + " "+dirs[this.dir]; +}; +Drone.prototype.debug = function(){ + print(this.toString()); + return this; +}; +/* + do the bresenham thing +*/ +var _bresenham = function(x0,y0,radius, setPixel, quadrants){ + // + // credit: Following code is copied almost verbatim from + // http://en.wikipedia.org/wiki/Midpoint_circle_algorithm + // Bresenham's circle algorithm + // + var f = 1 - radius; + var ddF_x = 1; + var ddF_y = -2 * radius; + var x = 0; + var y = radius; + var defaultQuadrants = {topleft: true, topright: true, bottomleft: true, bottomright: true}; + quadrants = quadrants?quadrants:defaultQuadrants; + /* + II | I + ------------ + III | IV + */ + if (quadrants.topleft || quadrants.topright) + setPixel(x0, y0 + radius); // quadrant I/II topmost + if (quadrants.bottomleft || quadrants.bottomright) + setPixel(x0, y0 - radius); // quadrant III/IV bottommost + if (quadrants.topright || quadrants.bottomright) + setPixel(x0 + radius, y0); // quadrant I/IV rightmost + if (quadrants.topleft || quadrants.bottomleft) + setPixel(x0 - radius, y0); // quadrant II/III leftmost + + while(x < y) + { + // ddF_x == 2 * x + 1; + // ddF_y == -2 * y; + // f == x*x + y*y - radius*radius + 2*x - y + 1; + if(f >= 0) + { + y--; + ddF_y += 2; + f += ddF_y; + } + x++; + ddF_x += 2; + f += ddF_x; + if (quadrants.topright){ + setPixel(x0 + x, y0 + y); // quadrant I + setPixel(x0 + y, y0 + x); // quadrant I + } + if (quadrants.topleft){ + setPixel(x0 - x, y0 + y); // quadrant II + setPixel(x0 - y, y0 + x); // quadrant II + } + if (quadrants.bottomleft){ + setPixel(x0 - x, y0 - y); // quadrant III + setPixel(x0 - y, y0 - x); // quadrant III + } + if (quadrants.bottomright){ + setPixel(x0 + x, y0 - y); // quadrant IV + setPixel(x0 + y, y0 - x); // quadrant IV + } + } +}; +var _getStrokeDir = function(x,y){ + var absY = Math.abs(y); + var absX = Math.abs(x); + var strokeDir = 0; + if (y > 0 && absY >= absX) + strokeDir = 0 ; //down + else if (y < 0 && absY >= absX) + strokeDir = 1 ; // up + else if (x > 0 && absX >= absY) + strokeDir = 2 ; // left + else if (x < 0 && absX >= absY) + strokeDir = 3 ; // right + return strokeDir; +}; +/* + The daddy of all arc-related API calls - + if you're drawing anything that bends it ends up here. +*/ +var _arc2 = function( params ) { + + var drone = params.drone; + var orientation = params.orientation?params.orientation:"horizontal"; + var quadrants = params.quadrants?params.quadrants:{ + topright:1, + topleft:2, + bottomleft:3, + bottomright:4 + }; + var stack = params.stack?params.stack:1; + var radius = params.radius; + var strokeWidth = params.strokeWidth?params.strokeWidth:1; + drone.chkpt('arc2'); + var x0, y0, gotoxy,setPixel; + + if (orientation == "horizontal"){ + gotoxy = function(x,y){ return drone.right(x).fwd(y);}; + drone.right(radius).fwd(radius).chkpt('center'); + switch (drone.dir) { + case 0: // east + case 2: // west + x0 = drone.z; + y0 = drone.x; + break; + case 1: // south + case 3: // north + x0 = drone.x; + y0 = drone.z; + } + setPixel = function(x,y) { + x = (x-x0); + y = (y-y0); + if (params.fill){ + // wph 20130114 more efficient esp. for large cylinders/spheres + if (y < 0){ + drone + .fwd(y).right(x) + .cuboidX(params.blockType,params.meta,1,stack,Math.abs(y*2)+1) + .back(y).left(x); + } + }else{ + if (strokeWidth == 1){ + gotoxy(x,y) + .cuboidX(params.blockType, + params.meta, + 1, // width + stack, // height + strokeWidth // depth + ) + .move('center'); + } else { + var strokeDir = _getStrokeDir(x,y); + var width = 1, depth = 1; + switch (strokeDir){ + case 0: // down + y = y-(strokeWidth-1); + depth = strokeWidth; + break; + case 1: // up + depth = strokeWidth; + break; + case 2: // left + width = strokeWidth; + x = x-(strokeWidth-1); + break; + case 3: // right + width = strokeWidth; + break; + } + gotoxy(x,y) + .cuboidX(params.blockType, params.meta, width, stack, depth) + .move('center'); + + } + } + }; + }else{ + // vertical + gotoxy = function(x,y){ return drone.right(x).up(y);}; + drone.right(radius).up(radius).chkpt('center'); + switch (drone.dir) { + case 0: // east + case 2: // west + x0 = drone.z; + y0 = drone.y; + break; + case 1: // south + case 3: // north + x0 = drone.x; + y0 = drone.y; + } + setPixel = function(x,y) { + x = (x-x0); + y = (y-y0); + if (params.fill){ + // wph 20130114 more efficient esp. for large cylinders/spheres + if (y < 0){ + drone + .up(y).right(x) + .cuboidX(params.blockType,params.meta,1,Math.abs(y*2)+1,stack) + .down(y).left(x); + } + }else{ + if (strokeWidth == 1){ + gotoxy(x,y) + .cuboidX(params.blockType,params.meta,strokeWidth,1,stack) + .move('center'); + }else{ + var strokeDir = _getStrokeDir(x,y); + var width = 1, height = 1; + switch (strokeDir){ + case 0: // down + y = y-(strokeWidth-1); + height = strokeWidth; + break; + case 1: // up + height = strokeWidth; + break; + case 2: // left + width = strokeWidth; + x = x-(strokeWidth-1); + break; + case 3: // right + width = strokeWidth; + break; + } + gotoxy(x,y) + .cuboidX(params.blockType, params.meta, width, height, stack) + .move('center'); + + } + } + }; + } + /* + setPixel assumes a 2D plane - need to put a block along appropriate plane + */ + _bresenham(x0,y0,radius,setPixel,quadrants); + + params.drone.move('arc2'); +}; + + +Drone.extend('arc',function(params) { + params.drone = this; + _arc2(params); +}); + +var _cylinder0 = function(block,radius,height,exactParams){ + var arcParams = { + radius: radius, + fill: false, + orientation: 'horizontal', + stack: height, + }; + + if (exactParams){ + arcParams.blockType = exactParams.blockType; + arcParams.meta = exactParams.meta; + }else{ + var md = this._getBlockIdAndMeta(block); + arcParams.blockType = md[0]; + arcParams.meta = md[1]; + } + return this.arc(arcParams); +}; +var _cylinder1 = function(block,radius,height,exactParams){ + var arcParams = { + radius: radius, + fill: true, + orientation: 'horizontal', + stack: height, + }; + + if (exactParams){ + arcParams.blockType = exactParams.blockType; + arcParams.meta = exactParams.meta; + }else{ + var md = this._getBlockIdAndMeta(block); + arcParams.blockType = md[0]; + arcParams.meta = md[1]; + } + return this.arc(arcParams); +}; +var _paste = function(name) +{ + var ccContent = Drone.clipBoard[name]; + var srcBlocks = ccContent.blocks; + var srcDir = ccContent.dir; // direction player was facing when copied. + var dirOffset = (4 + (this.dir - srcDir)) %4; + var that = this; + + _traverse[this.dir].width(that,srcBlocks.length,function(ww){ + var h = srcBlocks[ww].length; + _traverseHeight(that,h,function(hh){ + var d = srcBlocks[ww][hh].length; + _traverse[that.dir].depth(that,d,function(dd){ + var b = srcBlocks[ww][hh][dd]; + var bm = that._getBlockIdAndMeta(b); + var cb = bm[0]; + var md = bm[1]; + // + // need to adjust blocks which face a direction + // + switch (cb) + { + // + // doors + // + case 64: // wood + case 71: // iron + // top half of door doesn't need to change + if (md < 8) { + md = (md + dirOffset) % 4; + } + break; + // + // stairs + // + case 53: // oak + case 67: // cobblestone + case 108: // red brick + case 109: // stone brick + case 114: // nether brick + case 128: // sandstone + case 134: // spruce + case 135: // birch + case 136: // junglewood + var dir = md & 0x3; + var a = Drone.PLAYER_STAIRS_FACING; + var len = a.length; + for (var c=0;c < len;c++){ + if (a[c] == dir){ + break; + } + } + c = (c + dirOffset) %4; + var newDir = a[c]; + md = (md >>2<<2) + newDir; + break; + // + // signs , ladders etc + // + case 23: // dispenser + case 54: // chest + case 61: // furnace + case 62: // burning furnace + case 65: // ladder + case 68: // wall sign + var a = Drone.PLAYER_SIGN_FACING; + var len = a.length; + for (var c=0;c < len;c++){ + if (a[c] == md){ + break; + } + } + c = (c + dirOffset) %4; + var newDir = a[c]; + md = newDir; + break; + } + putBlock(that.x,that.y,that.z,cb,md,that.world); + }); + }); + }); +}; +var _getDirFromRotation = function(r){ + // 0 = east, 1 = south, 2 = west, 3 = north + // 46 to 135 = west + // 136 to 225 = north + // 226 to 315 = east + // 316 to 45 = south + + r = (r + 360) % 360; // east could be 270 or -90 + + if (r > 45 && r <= 135) + return 2; // west + if (r > 135 && r <= 225) + return 3; // north + if (r > 225 && r <= 315) + return 0; // east + if (r > 315 || r < 45) + return 1; // south +}; +var _getBlockIdAndMeta = function(b){ + var defaultMeta = 0; + if (typeof b == 'string'){ + var bs = b; + var sp = bs.indexOf(':'); + if (sp == -1){ + b = parseInt(bs); + // wph 20130414 - use sensible defaults for certain blocks e.g. stairs + // should face the drone. + for (var i in blocks.stairs){ + if (blocks.stairs[i] === b){ + defaultMeta = Drone.PLAYER_STAIRS_FACING[this.dir]; + break; + } + } + return [b,defaultMeta]; + } + b = parseInt(bs.substring(0,sp)); + var md = parseInt(bs.substring(sp+1,bs.length)); + return [b,md]; + }else{ + // wph 20130414 - use sensible defaults for certain blocks e.g. stairs + // should face the drone. + for (var i in blocks.stairs){ + if (blocks.stairs[i] === b){ + defaultMeta = Drone.PLAYER_STAIRS_FACING[this.dir]; + break; + } + } + return [b,defaultMeta]; + } +}; +// +// movement +// +var _movements = [{},{},{},{}]; +// east +_movements[0].right = function(that,n){ that.z +=n; return that;}; +_movements[0].left = function(that,n){ that.z -=n; return that;}; +_movements[0].fwd = function(that,n){ that.x +=n; return that;}; +_movements[0].back = function(that,n){ that.x -= n; return that;}; +// south +_movements[1].right = _movements[0].back; +_movements[1].left = _movements[0].fwd; +_movements[1].fwd = _movements[0].right; +_movements[1].back = _movements[0].left; +// west +_movements[2].right = _movements[0].left; +_movements[2].left = _movements[0].right; +_movements[2].fwd = _movements[0].back; +_movements[2].back = _movements[0].fwd; +// north +_movements[3].right = _movements[0].fwd; +_movements[3].left = _movements[0].back; +_movements[3].fwd = _movements[0].left; +_movements[3].back = _movements[0].right; +var _traverse = [{},{},{},{}]; +// east +_traverse[0].width = function(that,n,callback){ + var s = that.z, e = s + n; + for (; that.z < e; that.z++){ + callback(that.z-s); + } + that.z = s; +}; +_traverse[0].depth = function(that,n,callback){ + var s = that.x, e = s+n; + for (;that.x < e;that.x++){ + callback(that.x-s); + } + that.x = s; +}; +// south +_traverse[1].width = function(that,n,callback){ + var s = that.x, e = s-n; + for (;that.x > e;that.x--){ + callback(s-that.x); + } + that.x = s; +}; +_traverse[1].depth = _traverse[0].width; +// west +_traverse[2].width = function(that,n,callback){ + var s = that.z, e = s-n; + for (;that.z > e;that.z--){ + callback(s-that.z); + } + that.z = s; +}; +_traverse[2].depth = _traverse[1].width; +// north +_traverse[3].width = _traverse[0].depth; +_traverse[3].depth = _traverse[2].width; +var _traverseHeight = function(that,n,callback){ + var s = that.y, e = s + n; + for (; that.y < e; that.y++){ + callback(that.y-s); + } + that.y = s; +}; +// +// standard fisher-yates shuffle algorithm +// +var _fisherYates = function( myArray ) { + var i = myArray.length; + if ( i == 0 ) return false; + while ( --i ) { + var j = Math.floor( Math.random() * ( i + 1 ) ); + var tempi = myArray[i]; + var tempj = myArray[j]; + myArray[i] = tempj; + myArray[j] = tempi; + } +}; +var _copy = function(name, w, h, d) { + var that = this; + var ccContent = []; + _traverse[this.dir].width(that,w,function(ww){ + ccContent.push([]); + _traverseHeight(that,h,function(hh){ + ccContent[ww].push([]); + _traverse[that.dir].depth(that,d,function(dd){ + var b = that.world.getBlockAt(that.x,that.y,that.z); + ccContent[ww][hh][dd] = b; + }); + }); + }); + Drone.clipBoard[name] = {dir: this.dir, blocks: ccContent}; +}; +var _garden = function(w,d) { + // make sure grass is present first + this.down().box(2,w,1,d).up(); + + // make flowers more common than long grass + var dist = {37: 3, // red flower + 38: 3, // yellow flower + '31:1': 2, // long grass + 0: 1 + }; + + return this.rand(dist,w,1,d); +}; + +var _rand = function(blockDistribution){ + if (!(blockDistribution.constructor == Array)){ + var a = []; + for (var p in blockDistribution){ + var n = blockDistribution[p]; + for (var i = 0;i < n;i++){ + a.push(p); + } + } + blockDistribution = a; + } + while (blockDistribution.length < 1000){ + // make array bigger so that it's more random + blockDistribution = blockDistribution.concat(blockDistribution); + } + _fisherYates(blockDistribution); + return blockDistribution; +}; +Drone.extend('rand',function(dist,w,h,d){ + var randomized = _rand(dist); + this.boxa(randomized,w,h,d); +}); +var _trees = { + oak: org.bukkit.TreeType.BIG_TREE , + birch: org.bukkit.TreeType.BIRCH , + jungle: org.bukkit.TreeType.JUNGLE, + spruce: org.bukkit.TreeType.REDWOOD +}; +for (var p in _trees) +{ + Drone.extend(p, function(v) { + return function() { + var block = this.world.getBlockAt(this.x,this.y,this.z); + if (block.typeId == 2){ + this.up(); + } + var treeLoc = new org.bukkit.Location(this.world,this.x,this.y,this.z); + var successful = treeLoc.world.generateTree(treeLoc,v); + if (block.typeId == 2){ + this.down(); + } + }; + }(_trees[p])); +} + +// +// Drone's clipboard +// +Drone.clipBoard = {}; +Drone.extend('garden',_garden); +Drone.extend('copy', _copy); +Drone.extend('paste',_paste); +Drone.extend('cylinder0',_cylinder0); +Drone.extend('cylinder', _cylinder1); +// +// wph 20130130 - make this a method - extensions can use it. +// +Drone.prototype._getBlockIdAndMeta = _getBlockIdAndMeta; + + diff --git a/src/main/javascript/plugins/drone/sphere.js b/src/main/javascript/plugins/drone/sphere.js new file mode 100644 index 0000000..58f9451 --- /dev/null +++ b/src/main/javascript/plugins/drone/sphere.js @@ -0,0 +1,268 @@ +var Drone = require('./drone'); +module.exports = Drone; + +/************************************************************************ +Drone.sphere() method +===================== +Creates a sphere. + +Parameters +---------- + + * block - The block the sphere will be made of. + * radius - The radius of the sphere. + +Example +------- +To create a sphere of Iron with a radius of 10 blocks... + + sphere( blocks.iron, 10); + +![sphere example](img/sphereex1.png) + +Spheres are time-consuming to make. You *can* make large spheres (250 radius) but expect the +server to be very busy for a couple of minutes while doing so. + +***/ +Drone.extend('sphere', function(block,radius) +{ + var lastRadius = radius; + var slices = [[radius,0]]; + var diameter = radius*2; + var bm = this._getBlockIdAndMeta(block); + + var r2 = radius*radius; + for (var i = 0; i <= radius;i++){ + var newRadius = Math.round(Math.sqrt(r2 - i*i)); + if (newRadius == lastRadius) + slices[slices.length-1][1]++; + else + slices.push([newRadius,1]); + lastRadius = newRadius; + } + this.chkpt('sphere'); + // + // mid section + // + this.up(radius - slices[0][1]) + .cylinder(block,radius,(slices[0][1]*2)-1,{blockType: bm[0],meta: bm[1]}) + .down(radius-slices[0][1]); + + var yOffset = -1; + for (var i = 1; i < slices.length;i++) + { + yOffset += slices[i-1][1]; + var sr = slices[i][0]; + var sh = slices[i][1]; + var v = radius + yOffset, h = radius-sr; + // northern hemisphere + this.up(v).fwd(h).right(h) + .cylinder(block,sr,sh,{blockType: bm[0],meta: bm[1]}) + .left(h).back(h).down(v); + + // southern hemisphere + v = radius - (yOffset+sh+1); + this.up(v).fwd(h).right(h) + .cylinder(block,sr,sh,{blockType: bm[0],meta: bm[1]}) + .left(h).back(h). down(v); + } + return this.move('sphere'); +}); +/************************************************************************ +Drone.sphere0() method +====================== +Creates an empty sphere. + +Parameters +---------- + + * block - The block the sphere will be made of. + * radius - The radius of the sphere. + +Example +------- +To create a sphere of Iron with a radius of 10 blocks... + + sphere0( blocks.iron, 10); + +Spheres are time-consuming to make. You *can* make large spheres (250 radius) but expect the +server to be very busy for a couple of minutes while doing so. + +***/ +Drone.extend('sphere0', function(block,radius) +{ +/* + this.sphere(block,radius) + .fwd().right().up() + .sphere(0,radius-1) + .back().left().down(); + +*/ + + var lastRadius = radius; + var slices = [[radius,0]]; + var diameter = radius*2; + var bm = this._getBlockIdAndMeta(block); + + var r2 = radius*radius; + for (var i = 0; i <= radius;i++){ + var newRadius = Math.round(Math.sqrt(r2 - i*i)); + if (newRadius == lastRadius) + slices[slices.length-1][1]++; + else + slices.push([newRadius,1]); + lastRadius = newRadius; + } + this.chkpt('sphere0'); + // + // mid section + // + //.cylinder(block,radius,(slices[0][1]*2)-1,{blockType: bm[0],meta: bm[1]}) + this.up(radius - slices[0][1]) + .arc({blockType: bm[0], + meta: bm[1], + radius: radius, + strokeWidth: 2, + stack: (slices[0][1]*2)-1, + fill: false + }) + .down(radius-slices[0][1]); + + var yOffset = -1; + var len = slices.length; + for (var i = 1; i < len;i++) + { + yOffset += slices[i-1][1]; + var sr = slices[i][0]; + var sh = slices[i][1]; + var v = radius + yOffset, h = radius-sr; + // northern hemisphere + // .cylinder(block,sr,sh,{blockType: bm[0],meta: bm[1]}) + this.up(v).fwd(h).right(h) + .arc({ + blockType: bm[0], + meta: bm[1], + radius: sr, + stack: sh, + fill: false, + strokeWidth: i