From cd7d243f04d372422cd74530081d45b76818865c Mon Sep 17 00:00:00 2001 From: dsc Date: Tue, 7 Dec 2010 01:51:13 -0800 Subject: [PATCH] Adds AI action freq timers --- css/lttl.css | 2 +- index.php | 10 ++- src/Y/class.cjs | 76 ++++++++------ src/Y/core.cjs | 8 +- src/Y/delegate.cjs | 19 +++- src/Y/type.cjs | 6 +- src/Y/types/array.cjs | 136 +++++++++++++++---------- src/Y/types/function.cjs | 57 ++++++----- src/Y/types/object.cjs | 160 ++++++++++++++++++------------ src/Y/y.cjs | 18 ++-- src/evt.cjs | 243 ++++++++++++++++++++++++++++++++++++++++++++ src/evt/class.cjs | 246 --------------------------------------------- src/evt/evt.js.php | 40 -------- src/tanks/thing/tank.cjs | 45 ++++++-- src/tanks/thing/thing.cjs | 22 +++-- tags.html | 6 +- tanks.php | 132 ------------------------ 17 files changed, 589 insertions(+), 637 deletions(-) create mode 100644 src/evt.cjs delete mode 100644 src/evt/class.cjs delete mode 100644 src/evt/evt.js.php delete mode 100644 tanks.php diff --git a/css/lttl.css b/css/lttl.css index 23b3e57..b3d805f 100644 --- a/css/lttl.css +++ b/css/lttl.css @@ -57,7 +57,7 @@ table.grid td { /* outline:1px solid rgba(255,255,255,0.1); */ #welcome legend { padding:0.5em; text-transform:uppercase; } */ -#debug { position:relative; top:1em; right:1em; } +#debug { position:relative; top:1em; right:1em; z-index:500; } #debug .inner { position:absolute; top:0; right:0; padding:1em; } #debug .inner > * { float:right; margin-right:1em; } diff --git a/index.php b/index.php index 8d435d9..6fe8590 100644 --- a/index.php +++ b/index.php @@ -63,8 +63,14 @@
&1"); + echo "\n-->\n"; + + include "tags.html"; ?>
diff --git a/src/Y/class.cjs b/src/Y/class.cjs index 5e90e10..0aa02c8 100644 --- a/src/Y/class.cjs +++ b/src/Y/class.cjs @@ -2,18 +2,18 @@ var type = require('Y/type') , core = require('Y/core') -, YFunction = require('Y/types/function').YFunction +, yfn = require('Y/types/function') +, YFunction = yfn.YFunction +, unwrap = yfn.unwrap , isFunction = type.isFunction , slice = core.slice , globals = (function(){ return this; })() , _Object = globals.Object , _Function = globals.Function -, _Array = globals.Array , _String = globals.String , _Number = globals.Number -, slice = _Array.prototype.slice , hasOwn = _Object.prototype.hasOwnProperty , getProto = _Object.getPrototypeOf @@ -21,15 +21,17 @@ var type = require('Y/type') , classToString = function toString(){ return this.className+"()"; } ; -// Private delegating constructor -- must be defined for every -// new class to prevent shared state. All construction is -// actually done in the init method. +/** + * @private Delegating constructor -- must be defined for every new class to + * prevent shared state. All construction is actually done in the init method. + */ function _Class() { - var cls = arguments.callee - , instance = this; + var cls = arguments.callee + , instance = this + ; - // Not subclassing - if ( cls.caller !== Class.fabricate ) { + // Not fabricating/subclassing + if ( unwrap(cls.caller) !== fabricate ) { if ( instance.init ){ var result = instance.init.apply(instance, arguments); if (result) instance = result; @@ -148,34 +150,44 @@ Class.className = Class.fn.className = "Class"; * Create a new instance and run delegate constructor if it exists. * Unlike the keyword `new`, instantiate can be applied. */ -Class.instantiate = - function instantiate(){ - var instance = this.fabricate(); - if ( instance.init ) { - var r = instance.init.apply(instance, arguments); - return (r !== undefined ? r : instance); - } else - return instance; - }; - +function instantiate(cls){ + var instance = cls.fabricate(); + if ( instance.init ) { + var r = instance.init.apply(instance, slice.call(arguments,1)); + return (r !== undefined ? r : instance); + } else + return instance; +} + + /** * Create new instance, but do not run the delegate constructor. */ -Class.fabricate = - function fabricate(){ - return new this(); - }; +function fabricate(cls){ + return new cls(); +} /** - * Class method of Classes, not to be confused with Y.subclass, which is a static method. + * As a class method, subclass simply creates a subclass of that class. + * As an instance method, this method creates a subclass using that object as the prototype. + * Note that still different is Y.subclass, which simply creates a new Y.Class. */ +function subclass(className, members){ + return new Class(className, this, members); +} + Class.subclass = -Class.fn.subclass = - function subclass(className, members){ - return new Class(className, this, members); - }; +Class.fn.subclass = YFunction(subclass); +[ fabricate, instantiate +].forEach(function(fn){ + fn = YFunction(fn); + var name = fn.getName(); + exports[name] = fn; + Class[name] = fn.methodize(); + }); + /** * Root-class for all Y-objects. @@ -185,10 +197,10 @@ var YBase = new Class("YBase", { }); - exports['Class'] = exports['subclass'] = Class; -exports['instantiate'] = Class.instantiate.bind(Class); -exports['fabricate'] = Class.fabricate.bind(Class); exports['YBase'] = YBase; +// exports['instantiate'] = instantiate; +// exports['fabricate'] = fabricate; + diff --git a/src/Y/core.cjs b/src/Y/core.cjs index 72fa583..620723c 100644 --- a/src/Y/core.cjs +++ b/src/Y/core.cjs @@ -88,10 +88,14 @@ function attr(o, key, value, def){ } function extend( A, B ){ - return slice.call(arguments,1).reduce(extendall, A); + var args = slice.call(arguments,1); + if ( type.isArray(A) ) + return A.concat.apply(A, args); + else + return args.reduce(extendall, A); } function extendall(A, donor){ return reduce(donor, attrvk, A); } -function attrvk(o, v, k){ return attr(o, k, v, o[k]); } +function attrvk(o, v, k){ return attr(o, k, v, o[k]); } exports['reduce'] = reduce; diff --git a/src/Y/delegate.cjs b/src/Y/delegate.cjs index 016be83..8bc9e64 100644 --- a/src/Y/delegate.cjs +++ b/src/Y/delegate.cjs @@ -87,17 +87,26 @@ function attr(o, key, value, def){ if ( type.isPlainObject(key) ) return extend(o, key); - if ( value !== undefined || def !== undefined ){ + if ( value !== undefined || def !== undefined ) return set(o, key, value, def); - } else - return o[key]; + + return o[key]; } function extend( A, B ){ - return slice.call(arguments,1).reduce(extendall, A); + var args = slice.call(arguments,1); + if ( !A ) return A; + + if ( notWrapped(A.extend) ) + return A.extend.apply(A, args); + + if ( type.isArray(A) ) + return A.concat.apply(A, args); + + return args.reduce(extendall, A); } function extendall(A, donor){ return reduce(donor, attrvk, A); } -function attrvk(o, v, k){ return attr(o, k, v, o[k]); } +function attrvk(o, v, k){ return attr(o, k, v, o[k]); } exports['reduce'] = reduce; diff --git a/src/Y/type.cjs b/src/Y/type.cjs index 966aab8..9ff3782 100644 --- a/src/Y/type.cjs +++ b/src/Y/type.cjs @@ -36,8 +36,8 @@ function type_of(obj){ class2name[ toString.call(obj) ] || "object"; } -// isArray.types = []; -function isArray(obj) { return type_of(obj) === "array" /*|| obj instanceof Y.YArray*/ ; } +var arrayLike = isArray.types = []; +function isArray(obj) { return type_of(obj) === "array" || (arrayLike.indexOf(type(obj)) !== -1); } function isFunction(obj) { return type_of(obj) === "function"; } function isString(obj) { return type_of(obj) === "string"; } function isNumber(obj) { return type_of(obj) === "number"; } @@ -93,7 +93,7 @@ function type( o ) { function is( A, B ){ if ( isArray(B) ) - return B.map( is.bind(this,A) ).any(); // XXX: implicitly depends on YFunction, but we'll just quietly not use it + return B.some( is.bind(this,A) ); else { var AT = type(A), BT = type(B); return (A instanceof BT || B instanceof AT || AT === BT); diff --git a/src/Y/types/array.cjs b/src/Y/types/array.cjs index abbab0c..c9f11e8 100644 --- a/src/Y/types/array.cjs +++ b/src/Y/types/array.cjs @@ -1,58 +1,95 @@ -var YCollection = require('Y/types/collection').YCollection -, mixin = require('Y/utils').mixin -, slice = require('Y/core').slice +var Y = require('Y/y').Y // I *think* this is safe +, del = require('Y/delegate') +, type = require('Y/type') +, mixin = require('Y/utils').mixin +, YCollection = require('Y/types/collection').YCollection + +, _Array = Array +, AP = _Array.prototype +, slice = AP.slice +, arrayToString = AP.toString ; exports['YArray'] = YCollection.subclass('YArray', function(YArray){ - mixin(YArray, { donor:Array, chain:true, + // Add YArray to the things that count as arrays + type.isArray.types.push(YArray); + + mixin(YArray, { donor:_Array, chain:true, names:'push unshift sort splice reverse'.split(' ') }); - mixin(YArray, { donor:Array, wrap:YArray.instantiate.bind(YArray), + mixin(YArray, { donor:_Array, wrap:YArray.instantiate.bind(YArray), names:'map forEach filter slice'.split(' ') }); - mixin(YArray, { donor:Array, + mixin(YArray, { donor:_Array, names:'reduce some every indexOf lastIndexOf shift pop join'.split(' ') }); - this.init = function initYArray(o){ + this['init'] = + function initYArray(o){ this._o = o || []; }; - this.merge = - this.concat = + this['clone'] = + function clone(){ + return new YArray(this._o.slice(0)); + }; + + this['size'] = + function size(){ + return this._o.length; + }; + + this['toString'] = + function toString(){ + return "Y[" + arrayToString.call(this._o) + "]"; + }; + + this['last'] = + function last(){ + var A = this._o, L = A.length; + return ( L ? A[L-1] : undefined ); + }; + + function unwrapY(o){ + return (o instanceof YArray ? o.end() : o); + } + + this['concat'] = function concat( donor ){ - return new YArray( slice.call(arguments).reduce(function(A, donor ){ - return A.concat(donor instanceof YArray ? donor.end() : donor); - }, this._o) ); + var A = this._o; + return new YArray(A.concat.apply( A, slice.call(arguments,0).map(unwrapY) )); + // return new YArray( slice.call(arguments).reduce(function(A, donor ){ + // return A.concat(donor instanceof YArray ? donor.end() : donor); + // }, this._o) ); + }; + + this['extend'] = + function extend( donor ){ + var A = this._o; + this._o = A.concat.apply( A, slice.call(arguments,0).map(unwrapY) ); + return this; }; - this.remove = + this['remove'] = function remove(v){ - if (arguments.length > 1) - slice.call(arguments).forEach(this.remove, this); - else { + var L = arguments.length; + if (L == 1) { var idx = this.indexOf(v); if ( idx != -1 ) this.splice(idx, 1); - } + } else if (L > 1) + slice.call(arguments,0).forEach(this.remove, this); return this; }; - this.last = - function last(){ - var A = this._o; - return A[ A.length-1 ]; - }; - - this.unique = + this['unique'] = function unique(){ return this.reduce( function(acc, v, i){ if (acc.indexOf(v) === -1) acc.push(v); return acc; - - }, new YArray() ); + }, new YArray(), this ); // return this.filter(function(v, i){ // // Executes in the context of the new array, so @@ -70,39 +107,30 @@ YCollection.subclass('YArray', function(YArray){ // }).invoke('end'); // }; - // Like map, but produces a dict - // Y(["Foo?", "R&D"]).generate(encodeURIComponent) - // -> Y({"Foo?":"Foo%3F", "R&D":"R%26D"}) - this.generate = - function generate(fn, acc){ - var args = slice.call(arguments), - fn = args.shift(), - acc = args.shift(); - return this.reduce(function(acc, v, i){ - return acc.attr(v, fn.apply( this, [v].concat(args) )); - }, Y(acc || {})); + /** + * Like map, but produces a YObject: + * Y(["Foo?", "R&D"]).generate(encodeURIComponent) + * -> + * Y({ + * "Foo?" : "Foo%3F", + * "R&D" : "R%26D" + * }) + */ + this['generate'] = + function generate(fn, cxt){ + // var args = slice.call(arguments,2); + return this.reduce(function(dict, v){ + return dict.attr(v, fn.call(this,v)); + }, Y({}), cxt||this ); }; // Y([ ["foo", 1], ["bar", 2] ]).toDict() // -> Y({foo:1, bar:2}) - this.toDict = + this['toDict'] = function toDict(){ - - }; - - this.clone = - function clone(){ - return new YArray(this._o.slice(0)); - }; - - this.size = - function size(){ - return this._o.length; - }; - - this.toString = - function toString(){ - return "Y[" + (this._o || "") + "]"; + return this._o.reduce(function(dict, kv){ + return dict.attr(kv[0], kv[1]); + }, Y({}), this ); }; return this; diff --git a/src/Y/types/function.cjs b/src/Y/types/function.cjs index 05d704d..c16d3a0 100644 --- a/src/Y/types/function.cjs +++ b/src/Y/types/function.cjs @@ -9,8 +9,9 @@ var undefined , isFunction = type.isFunction , slice = core.slice -, _ = YFunction._ = {} -, YFP = YFunction.prototype +, YF = YFunction +, _ = YF._ = {} +, YFP = YF.prototype ; function YFunction(fn){ @@ -30,7 +31,7 @@ function YFunction(fn){ } core.extend( YFP, { - init : YFunction, + init : YF, attr : methodize(core.attr), reduce : methodize(core.reduce), extend : methodize(core.extend), @@ -40,7 +41,7 @@ core.extend( YFP, { function wraps(wrapper, fn) { wrapper[WRAPS] = fn; - return YFunction(wrapper); + return YF(wrapper); } function unwrap(fn){ @@ -127,7 +128,7 @@ function chain(f,g){ return chained; } -// YFunction.prototype.lazy = methodize(lazy); +// YF.prototype.lazy = methodize(lazy); // function lazy(fn){ // var args = slice.call(arguments, 1) // , L = unwrap(fn).length @@ -207,7 +208,7 @@ function memoize(fn){ // Memorized to reduce eval costs var _ofArityWrapper = -YFunction._ofArityWrapper = +YF._ofArityWrapper = memoize(function(n, limit){ var i = n, args = []; while (i-- > 0) args.unshift('$'+i); // Can't use Y.range due to deps @@ -248,7 +249,7 @@ function limit(fn, n){ /** * Filter the arguments passed to the wrapper function */ -// YFunction.prototype.mask = mask; +// YF.prototype.mask = mask; // function mask(){ // // } @@ -269,24 +270,32 @@ function getName( fn ){ -exports['wraps'] = wraps; -exports['unwrap'] = unwrap; -exports['curry'] = curry; -exports['methodize'] = methodize; -exports['genericize'] = genericize; -exports['compose'] = compose; -exports['chain'] = chain; -exports['bind'] = bind; -exports['partial'] = partial; -exports['memoize'] = memoize; -exports['aritize'] = aritize; -exports['limit'] = limit; -exports['getName'] = getName; - -// Methodize and then attach to YFunction's prototype -YFP.extend(core.map(exports, methodize)); +// exports['wraps'] = wraps; +// exports['unwrap'] = unwrap; +// exports['curry'] = curry; +// exports['methodize'] = methodize; +// exports['genericize'] = genericize; +// exports['compose'] = compose; +// exports['chain'] = chain; +// exports['bind'] = bind; +// exports['partial'] = partial; +// exports['memoize'] = memoize; +// exports['aritize'] = aritize; +// exports['limit'] = limit; +// exports['getName'] = getName; + + +[ wraps, unwrap, getName, + curry, compose, chain, bind, partial, + methodize, genericize, memoize, aritize, limit +].map(function(fn){ + var name = getName(fn); + YFP[name] = methodize(fn); + exports[name] = fn; + return fn; +}).forEach(YF); // Have to do this AFTER attaching to the prototype // Export these last to avoid methodizing them -exports['YFunction'] = YFunction; +exports['YFunction'] = YF(YF); exports._ = _; diff --git a/src/Y/types/object.cjs b/src/Y/types/object.cjs index 07db9d7..42a2a60 100644 --- a/src/Y/types/object.cjs +++ b/src/Y/types/object.cjs @@ -1,75 +1,107 @@ var YCollection = require('Y/types/collection').YCollection -, type = require('Y/type') -, isArray = type.isArray +, YFunction = require('Y/types/function').YFunction +, core = require('Y/core') +, del = require('Y/delegate') +, type = require('Y/type') ; +/** + * Ensures an object has the given property or nested property chain. Each missing + * link in a chain is filled with a new, empty object. + * @param {Object} base The object to search. + * @param {String} chain Property name or nested property chain starting at `base`. + * Property chains are specified in dotted lookup syntax. + * @param {Function} [def] If supplied, this function will be called with no + * arguments to get an object to insert into a missing link. + * @return {Object} base + */ +function ensure(base, chain, def){ + if (base && chain) + chain.split('.').reduce(ensurer, { 'obj':base, 'def':def }); + return base; +} +function ensurer(meta, link) { + var o = meta.obj + , def = meta.def + , next = del.attr(o, link); + if (next === undefined){ + next = def ? def() : {}; + del.attr(o, link, next); + } + meta.obj = next; + return meta; +} -exports['YObject'] = -YCollection.subclass('YObject', { - - 'init': function initYObject(o){ - this._o = o || {}; - }, +/** + * Searches an object for a given subkey specified in dotted-property syntax. + * @param {Object} o Object to be searched. + * @param {Array|String} chain The property-chain to lookup. + * @return {Object|undefined} If chain is found, an object of the form: + * `{ 'key': Qualified key name, 'obj': Parent object of key, 'val': Value at obj[key] }` + * and `undefined` otherwise. + */ +function getNestedMeta(o, chain){ + if ( !type.isArray(chain) ) + chain = chain.toString().split('.'); + return chain.reduce(metaGetter, { 'val':o }); +} +function metaGetter(current, link){ + var val = current ? current.val : undefined + , next = del.attr(val,link) ; - /** - * Ensures an object has the given property or nested property chain. Each missing link in a chain is filled with a new, empty object. - * @param {String} chain Property name or nested property chain starting at `base`. Property chains are specified in dotted lookup syntax. - * @return {this} - */ - ensure : function ensure(chain){ - if (chain) chain - .split('.') - .reduce(function(o,link) { - if( o && link && o[link] === undefined ) - o[link] = {}; - return o[link]; - }, this._o ); - return this; - }, + if ( next === undefined ) + return undefined; - /** - * Searches a heirarchical object for a given subkey specified in dotted-property syntax. - * @param {Array|String} chain The property-chain to lookup. - * @param {Any} [def] Default value should the key be `undefined`. - * @param {Boolean} [meta] If supplied return an object of the form - * `{ key: Qualified key name, obj: Parent object of key, value: Value at obj[key] }` - * if chain is found (and `undefined` otherwise). - * @return {Any} The value at the path, or `def` if `undefined`, otherwise `undefined`. - */ - getNested : function getNested(chain, def, meta){ - if ( !isArray(chain) ) - chain = chain.toString().split('.'); - - var lastIdx = chain.length-1 - , ret = chain.reduce(function(current, key){ - if ( current === undefined || current.value[key] === undefined ) - return undefined; - else - return { - key : key, - value : current.value[key], - obj : current.value - }; - }, { value:this._o }); - - if (ret !== undefined) - return (meta ? ret : ret.value); - else - return (meta ? { value:def } : def); - }, - - /** - * Searches a heirarchical object for a given subkey specified in dotted-property - * syntax, setting it wiht the provided value if found. - * @param {Array|String} chain The property-chain to lookup. - * @param {Any} value The value to set. - * @return {this} - */ - setNested : function setNested(chain, value){ - var prop = this.ensure(chain).getNested(chain); + return { 'key':link, 'val':next, 'obj':val }; +} + +/** + * Searches an object for a given subkey specified in dotted-property syntax. + * @param {Object} o Object to be searched. + * @param {Array|String} chain The property-chain to lookup. + * @param {Any} [def] Default value to return should the key be `undefined`. + * @return {Any} The value at the path, or `def` if `undefined`, otherwise `undefined`. + */ +function getNested(o, chain, def){ + var ret = getNestedMeta(o, chain); + return (ret ? ret.val : def); +} + +/** + * Sets `value` at the nested subkey specified in dotted-property syntax. + * Intermediate objects will be created as necessary. + * @param {Object} o Object to be searched. + * @param {Array|String} chain The property-chain to lookup. + * @param {Any} value The value to set. + * @return {Object} o + */ +function setNested(o, chain, value){ + if (o && chain){ + var prop = getNestedMeta(ensure(o,chain), chain); prop.obj[prop.key] = value; - return this; } + return o; +} + +var YObject = +exports['YObject'] = +YCollection.subclass('YObject', function setupYObject(YObject){ + + this['init'] = + function initYObject(o){ + this._o = o || {}; + }; }); + +core.forEach({ + 'ensure' : ensure, + 'metaGetter' : metaGetter, + 'getNested' : getNested, + 'setNested' : setNested +}, function(fn, name){ + fn = exports[name] = YFunction(fn); + YObject.fn[name] = fn.methodize(); + }); + diff --git a/src/Y/y.cjs b/src/Y/y.cjs index a9018a8..57bb49d 100644 --- a/src/Y/y.cjs +++ b/src/Y/y.cjs @@ -17,12 +17,8 @@ var undefined , toString = OP.toString , hasOwn = OP.hasOwnProperty -, core = require('Y/core') -, extend = core.extend - +, del = require('Y/delegate') , type = require('Y/type') -, type_of = type.type_of, isNumber = type.isNumber -, isPlainObject = type.isPlainObject, isArray = type.isArray ; /** @@ -45,7 +41,7 @@ function Y(o){ // Y( arguments, start=0, stop=arguments.length ) // Cast `arguments` object to a real Array, optionally slicing at specified delimiters if ( o.prototype === undefined - && isNumber(o.length) + && type.isNumber(o.length) && !_Array.isArray(o) && o.constructor === _Object ) { @@ -60,12 +56,12 @@ function Y(o){ // Merge Arrays or Objects // Y([0,1], [2,3], [4,5]) -> [0,1,2,3,4,5] // Y({foo:1}, {bar:2}) -> { foo:1, bar:2 } - if ( args.every(isArray) || args.every(isPlainObject) ) { - r = extend.apply(this, args); + if ( args.every(type.isArray) || args.every(type.isPlainObject) ) { + r = del.extend.apply(this, args); // Convenience of Y(Y.range()) - } else if ( args.every(isNumber) ) { - r = Y.range.apply(this, A); + } else if ( args.every(type.isNumber) ) { + r = Y.range.apply(this, args); // We got random stuff: wrap an Array of it } else @@ -75,7 +71,7 @@ function Y(o){ } // Do we have a type-specific wrapper? - var name = type_of(o) + var name = type.type_of(o) , yname = 'Y' + name.charAt(0).toUpperCase() + name.slice(1) , YType = Y[yname] ; diff --git a/src/evt.cjs b/src/evt.cjs new file mode 100644 index 0000000..14b0090 --- /dev/null +++ b/src/evt.cjs @@ -0,0 +1,243 @@ +// Inspired by John Resig's "Simple Class Inheritence" -- http://ejohn.org/blog/simple-javascript-inheritance/ +/* +Metaprogramming API + +Events +- "create" -- Fired when a new class instance is created. Data: self +- "destroy" -- Fired when an instance is destoryed. Data: self +- "init" -- Fired when the initialiser is invoked. Note that subclasses of the listened class may fire this event. Data: self +- "subclass" -- Fired when a new subclass is created. Data: parent, self, body + +Customization Protocols +- init() -- Initialiser; fires "init" event. +- destroy() -- Should clean up resources and release weak references; fires "destroy" event. +- extended(name, child, body) -- Called when a new subclass is created. Returns new child class. + +Class Methods +- subclass(name, body) -- Fires "subclass" event. +- fabricate() -- Generates a new class instance but does not inistialise it. +- instantiate([args...]) +- methods() +- properties() + +Note: Metaprogramming events cannot depend on Class. +*/ + +var Y = require('Y').Y +, Emitter = require('Y/modules/y.event').Emitter +, unwrap = require('Y/types/function').unwrap + +, isFunction = Y.isFunction +, KNOWN_CLASSES = Class.KNOWN_CLASSES = exports['KNOWN_CLASSES'] = {} +, getProto = Object.getPrototypeOf +, hasOwn = Object.prototype.hasOwnProperty +, objToString = Object.prototype.toString +, classToString = function toString(){ return this.className+"()"; } +; + +// Private delegating constructor -- must be defined for every +// new class to prevent shared state. All construction is +// actually done in the init method. +exports['ConstructorTemplate'] = ConstructorTemplate; +function ConstructorTemplate() { + var cls = arguments.callee + , instance = this; + + // Not subclassing + if ( unwrap(cls.caller) !== Y.fabricate ) { + + cls.fire('create', instance, { + 'instance' : instance, + 'cls' : cls, + 'args' : Y(arguments) + }); + + if ( instance.initialise ) + return instance.initialise.apply(instance, arguments); + } + + return instance; +} + + + + + +/** + * Creates a new class. All classes inherit from Evt.Class, and therefore support + * metaprogramming hooks and mixins. + * + * @param {String} className Name of the Class. + * + * @param {Type} [Parent] Parent class for subclassing. If Parent is a subclass of Evt.Class + * itself, the new class will inherit directly from Parent. Otherwise, members will be + * copied off the prototype of Parent before applying any new instance members, but the + * new class will not respond to instanceof from Parent. + * + * @param {Object} [members] Instance members to add to the new class's prototype; a class + * constructor can be supplied as `init`. + * + * @returns {Class} A new Class. + */ +function Class(className, Parent, members){ + var ClassFactory = arguments.callee + , SuperClass = ClassFactory + , prototype = this + , parentMembers = {} + ; + + if ( !members && !isFunction(Parent) ) { + members = Parent; + Parent = null; + } + members = members || {}; + Parent = Parent || getProto(members).constructor || ClassFactory; + + if (Parent == ClassFactory) + Parent = Object; + + // Parent is the prototype + if ( !isFunction(Parent) ) { + SuperClass = getProto(Parent).constructor || Object; + prototype = Parent; + + // Parent is a constructor: check ClassFactory + } else if (Parent.prototype instanceof ClassFactory) { + SuperClass = Parent; + prototype = Parent.fabricate(); + + // Recurse so `this` is an instance of ClassFactory + } else if ( !(prototype instanceof ClassFactory) ) { + return new ClassFactory(className, Parent, members); + + } else { + parentMembers = Parent.prototype || {}; + } + + + // Creates a new function with the appropriate name + // based on the className. + var NewClass, + constructor = [ + 'var '+className, + (''+ConstructorTemplate).replace(ConstructorTemplate.name, className), + 'NewClass = '+className, '' + ].join(';\n'); + + eval(constructor); + + // Copy Class statics + for (var k in ClassFactory) { + var v = ClassFactory[k]; + if ( isFunction(v) && !(k in Y.event.Emitter.methods) ) + NewClass[k] = v; + } + + // Copy parent methods + for (var k in parentMembers) + prototype[k] = parentMembers[k]; + + if ( prototype.toString === objToString ) + prototype.toString = classToString; + + NewClass.className = prototype.className = className; + NewClass.prototype = NewClass.fn = prototype; + + // Fix Constructors + NewClass.__super__ = SuperClass; // don't override NewClass.constructor -- it should be Function + prototype.constructor = prototype.__class__ = NewClass; + + NewClass.init = // XXX: This means subclasses will fire events. + prototype.initialise = + function initialise(){ + var instance = this + , init = NewClass.prototype.init + ; + + instance.__emitter__ = new Emitter(instance, NewClass); + + if (init) { + var result = init.apply(instance, arguments); + if (result) instance = result; + } + + instance.fire('init', instance, { + 'instance' : instance, + 'cls' : NewClass, + 'args' : Y(arguments) + }); + + return instance; + }; + + // Add class emitter + var ParentEmitter = (Parent.__emitter__ ? Parent : ClassFactory) + , ClassEmitter = NewClass.__emitter__ = new Emitter(NewClass, ParentEmitter) + ; + + // Either invoke body constructor... + if ( isFunction(members) ) { + members.call(prototype, NewClass); + + // Or add new instance methods + } else for (var k in members) { + if ( hasOwn.call(members, k) ) + prototype[k] = members[k]; + } + + // Record for metaprogramming + KNOWN_CLASSES[className] = NewClass; + + // Notify parent of the subclass + ParentEmitter.fire('subclass', + NewClass, { + 'className' : className, + 'parent' : Parent, + 'child' : NewClass, + 'members' : members, + 'prototype' : prototype + }); + + return NewClass; +} + +// Decorate with emitter methods and +// add metaprogramming data to Class object +Class.__super__ = Object; +Class.__emitter__ = new Emitter(Class); +Class.fn = Class.prototype; +Class.fn.__class__ = Class; +Class.className = Class.fn.className = "Class"; + +/* Class Methods */ + +/** + * Create a new instance and run delegate constructor if it exists. + * Unlike the keyword `new`, instantiate can be applied. + */ +function instantiate(){ + var instance = this.fabricate(); + if ( instance.initialise ) + instance.initialise.apply(instance, arguments); + return instance; +} + +Class.instantiate = Y(instantiate).methodize(); +Class.fabricate = Y.Class.fabricate; + +/** + * Class/Instance method of Classes, not to be confused with Evt.subclass, which is a static method. + */ +Class.subclass = +Class.fn.subclass = + Y(function subclass(className, members){ + return new Class(className, this, members); + }); + + +// Expose +exports['Class'] = +exports['subclass'] = Class; +exports['instantiate'] = instantiate; +exports['fabricate'] = Y.fabricate; + diff --git a/src/evt/class.cjs b/src/evt/class.cjs deleted file mode 100644 index dfc3701..0000000 --- a/src/evt/class.cjs +++ /dev/null @@ -1,246 +0,0 @@ -// Inspired by John Resig's "Simple Class Inheritence" -- http://ejohn.org/blog/simple-javascript-inheritance/ -/* -Metaprogramming API - -Events -- "create" -- Fired when a new class instance is created. Data: self -- "destroy" -- Fired when an instance is destoryed. Data: self -- "init" -- Fired when the initialiser is invoked. Note that subclasses of the listened class may fire this event. Data: self -- "subclass" -- Fired when a new subclass is created. Data: parent, self, body - -Customization Protocols -- init() -- Initialiser; fires "init" event. -- destroy() -- Should clean up resources and release weak references; fires "destroy" event. -- extended(name, child, body) -- Called when a new subclass is created. Returns new child class. - -Class Methods -- subclass(name, body) -- Fires "subclass" event. -- fabricate() -- Generates a new class instance but does not inistialise it. -- instantiate([args...]) -- methods() -- properties() - -Note: Metaprogramming events cannot depend on Class. -*/ - -var Y = require('Y').Y -, Emitter = require('Y/modules/y.event').Emitter - -, isFunction = Y.isFunction -, KNOWN_CLASSES = Class.KNOWN_CLASSES = exports['KNOWN_CLASSES'] = {} -, getProto = Object.getPrototypeOf -, hasOwn = Object.prototype.hasOwnProperty -, objToString = Object.prototype.toString -, classToString = function toString(){ return this.className+"()"; } -; - -// Private delegating constructor -- must be defined for every -// new class to prevent shared state. All construction is -// actually done in the init method. -exports['ConstructorTemplate'] = ConstructorTemplate; -function ConstructorTemplate() { - var cls = arguments.callee - , instance = this; - - // Not subclassing - if ( cls.caller !== Class.fabricate ) { - - cls.fire('create', instance, { - 'instance' : instance, - 'cls' : cls, - 'args' : Y(arguments) - }); - - if ( instance.initialise ) - return instance.initialise.apply(instance, arguments); - } - - return instance; -} - - - - - -/** - * Creates a new class. All classes inherit from Evt.Class, and therefore support - * metaprogramming hooks and mixins. - * - * @param {String} className Name of the Class. - * - * @param {Type} [Parent] Parent class for subclassing. If Parent is a subclass of Evt.Class - * itself, the new class will inherit directly from Parent. Otherwise, members will be - * copied off the prototype of Parent before applying any new instance members, but the - * new class will not respond to instanceof from Parent. - * - * @param {Object} [members] Instance members to add to the new class's prototype; a class - * constructor can be supplied as `init`. - * - * @returns {Class} A new Class. - */ -function Class(className, Parent, members){ - var ClassFactory = arguments.callee - , SuperClass = ClassFactory - , prototype = this - , parentMembers = {} - ; - - if ( !members && !isFunction(Parent) ) { - members = Parent; - Parent = null; - } - members = members || {}; - Parent = Parent || getProto(members).constructor || ClassFactory; - - if (Parent == ClassFactory) - Parent = Object; - - // Parent is the prototype - if ( !isFunction(Parent) ) { - SuperClass = getProto(Parent).constructor || Object; - prototype = Parent; - - // Parent is a constructor: check ClassFactory - } else if (Parent.prototype instanceof ClassFactory) { - SuperClass = Parent; - prototype = Parent.fabricate(); - - // Recurse so `this` is an instance of ClassFactory - } else if ( !(prototype instanceof ClassFactory) ) { - return new ClassFactory(className, Parent, members); - - } else { - parentMembers = Parent.prototype || {}; - } - - - // Creates a new function with the appropriate name - // based on the className. - var NewClass, - constructor = [ - 'var '+className, - (''+ConstructorTemplate).replace(ConstructorTemplate.name, className), - 'NewClass = '+className, '' - ].join(';\n'); - - eval(constructor); - - // Copy Class statics - for (var k in ClassFactory) { - var v = ClassFactory[k]; - if ( isFunction(v) && !(k in Y.event.Emitter.methods) ) - NewClass[k] = v; - } - - // Copy parent methods - for (var k in parentMembers) - prototype[k] = parentMembers[k]; - - if ( prototype.toString === objToString ) - prototype.toString = classToString; - - NewClass.className = prototype.className = className; - NewClass.prototype = NewClass.fn = prototype; - - // Fix Constructors - NewClass.__super__ = SuperClass; // don't override NewClass.constructor -- it should be Function - prototype.constructor = prototype.__class__ = NewClass; - - NewClass.init = // XXX: This means subclasses will fire events. - prototype.initialise = - function initialise(){ - var instance = this - , init = NewClass.prototype.init - ; - - instance.__emitter__ = new Emitter(instance, NewClass); - - if (init) { - var result = init.apply(instance, arguments); - if (result) instance = result; - } - - instance.fire('init', instance, { - 'instance' : instance, - 'cls' : NewClass, - 'args' : Y(arguments) - }); - - return instance; - }; - - // Add class emitter - var ParentEmitter = (Parent.__emitter__ ? Parent : ClassFactory) - , ClassEmitter = NewClass.__emitter__ = new Emitter(NewClass, ParentEmitter) - ; - - // Either invoke body constructor... - if ( isFunction(members) ) { - members.call(prototype, NewClass); - - // Or add new instance methods - } else for (var k in members) { - if ( hasOwn.call(members, k) ) - prototype[k] = members[k]; - } - - // Record for metaprogramming - KNOWN_CLASSES[className] = NewClass; - - // Notify parent of the subclass - ParentEmitter.fire('subclass', - NewClass, { - 'className' : className, - 'parent' : Parent, - 'child' : NewClass, - 'members' : members, - 'prototype' : prototype - }); - - return NewClass; -} - -// Decorate with emitter methods and -// add metaprogramming data to Class object -Class.__super__ = Object; -Class.__emitter__ = new Emitter(Class); -Class.fn = Class.prototype; -Class.fn.__class__ = Class; -Class.className = Class.fn.className = "Class"; - -/* Class Methods */ - -/** - * Create a new instance and run delegate constructor if it exists. - * Unlike the keyword `new`, instantiate can be applied. - */ -Class.instantiate = - function instantiate(){ - var instance = this.fabricate(); - if ( instance.initialise ) - instance.initialise.apply(instance, arguments); - return instance; - }; - -/** - * Create new instance, but do not run the delegate constructor. - */ -Class.fabricate = - function fabricate(){ - return new this(); - }; - -/** - * Class/Instance method of Classes, not to be confused with Evt.subclass, which is a static method. - */ -Class.subclass = -Class.fn.subclass = - function subclass(className, members){ - return new Class(className, this, members); - }; - - -// Expose -exports['Class'] = -exports['subclass'] = Class; - diff --git a/src/evt/evt.js.php b/src/evt/evt.js.php deleted file mode 100644 index c4ee4a0..0000000 --- a/src/evt/evt.js.php +++ /dev/null @@ -1,40 +0,0 @@ - 0) { - $f = fopen($path, "r"); - echo fread($f, $size); - fclose($f); - } - if ($add_newline) echo "\n"; -} - -$js_files = array( - 'evt.class' -); - -function evt_js_list($path='') { - global $js_files; - $path = $path ? $path : dirname($_SERVER["REQUEST_URI"]); - // echo $path; - foreach ($js_files as $f) { - echo "\n"; - } -} - -function evt_js_dump($expose=false) { - global $js_files; - if (!$expose) - echo "(function(){"; - foreach ($js_files as $f) - dump_file("./$f.js"); - if (!$expose) - echo "})();"; -} - -if ( basename($_SERVER["SCRIPT_FILENAME"]) == basename(__FILE__) ) { - if ( $_REQUEST["list"] ) - evt_js_list(); - else - evt_js_dump(); -} \ No newline at end of file diff --git a/src/tanks/thing/tank.cjs b/src/tanks/thing/tank.cjs index 10e2112..38f2251 100644 --- a/src/tanks/thing/tank.cjs +++ b/src/tanks/thing/tank.cjs @@ -3,6 +3,7 @@ var Y = require('Y').Y , Bullet = require('tanks/thing/bullet').Bullet , Trajectory = require('tanks/map/trajectory').Trajectory , Vec = require('ezl/math/vec').Vec +, Cooldown = require('ezl/loop').Cooldown , shape = require('ezl/shape') , Rect = shape.Rect , Circle = shape.Circle @@ -28,7 +29,7 @@ Thing.subclass('Tank', { height : REF_SIZE*0.55, // Attributes - stats: { + stats : { hp : 1, // health move : 0.75, // move speed (squares/sec) @@ -39,6 +40,15 @@ Thing.subclass('Tank', { shots : 4 // max projectiles in the air at once }, + // AI "Cooldowns" (max frequency of each action per sec) + ai : { + path : 1.0, // calculate a path to enemy + dodge : 1.0, // dodge an incoming bullet + shootIncoming : 0.5, // shoot down incoming bullet + shootEnemy : 0.75 // shoot at enemy tank if in range + }, + + nShots : 0, currentMove : null, forceCurrentMove : false, @@ -46,7 +56,7 @@ Thing.subclass('Tank', { - init : function init(align){ + init : function initTank(align){ Thing.init.call(this, align); this.onBulletDeath = this.onBulletDeath.bind(this); @@ -60,8 +70,16 @@ Thing.subclass('Tank', { + /* + ai : { + path : 1.0, // calculate a path to enemy + dodge : 1.0, // dodge an incoming bullet + shootIncoming : 0.5, // shoot down incoming bullet + shootEnemy : 0.75 // shoot at enemy tank if in range + */ act : function act(allotment){ + var ai = this.ai; // Check to see if we should obey our last decision, and not recalc if ( this.forceCurrentMove && this.forceCurrentMove() && this.currentMoveLimit > NOW ) { @@ -72,10 +90,11 @@ Thing.subclass('Tank', { // Try to shoot down nearby bullets - if ( this.ableToShoot() ) { + if ( ai.shootIncoming.ready && this.ableToShoot() ) { var bs = this.willCollide( this.findNearLike(25, isBullet) ); // console.log('['+TICKS+':'+this.id, this, '] Shoot down bullets?', bs.size() && bs); if ( bs.size() ) { + ai.shootIncoming.activate(NOW); var b = this.closestOf(bs); // console.log(' --> Incoming! Shoot it down!', b); this.shoot(b.loc.x, b.loc.y); @@ -84,19 +103,23 @@ Thing.subclass('Tank', { } // Dodge incoming bullet - var bs = this.willCollide(this.findNearLike(71, isBullet), 5); - // console.log('['+TICKS+':'+this.id, this, '] Dodge bullets?', bs.size() && bs); - if ( bs.size() ) { - var bullet = this.closestOf(bs); - this.moveAwayFrom(bullet); - return this; + if (ai.dodge.ready) { + var bs = this.willCollide(this.findNearLike(71, isBullet), 5); + // console.log('['+TICKS+':'+this.id, this, '] Dodge bullets?', bs.size() && bs); + if (bs.size()) { + ai.dodge.activate(NOW); + var bullet = this.closestOf(bs); + this.moveAwayFrom(bullet); + return this; + } } // Try to blow up nearby tanks - if ( this.ableToShoot() ) { + if (ai.shootEnemy.ready && (this.stats.shots - this.nShots > 1)) { var t = this.findNearEnemies(71, true).shift(); // console.log('['+TICKS+':'+this.id, this, '] Shoot at enemies?', t); if (t) { + ai.shootEnemy.activate(NOW); // console.log(' --> I gotcha!', t); this.shoot(t.loc.x, t.loc.y); return this; @@ -177,7 +200,7 @@ Thing.subclass('Tank', { }, willCollide : function willCollide(bullets, wiggle){ - bullets = ( !(Y.isArray(bullets) || bullets instanceof Y.YArray) ? [bullets] : bullets ); + bullets = ( Y.isArray(bullets) ? bullets : [bullets] ); wiggle = wiggle || 0; var tank = this, bb = this.boundingBox diff --git a/src/tanks/thing/thing.cjs b/src/tanks/thing/thing.cjs index 7d4503b..dc48f56 100644 --- a/src/tanks/thing/thing.cjs +++ b/src/tanks/thing/thing.cjs @@ -1,7 +1,7 @@ -//#ensure "evt/class" +//#ensure "evt" var Y = require('Y').Y -, Class = require('evt/class').Class +, evt = require('evt') , Loc = require('ezl/loc/loc').Loc , BoundingBox = require('ezl/loc/boundingbox').BoundingBox @@ -11,7 +11,7 @@ var Y = require('Y').Y Thing = exports['Thing'] = -new Class('Thing', { +new evt.Class('Thing', { init : function init(align){ this.id = Thing.THING_ID++; @@ -21,6 +21,7 @@ new Class('Thing', { this.createCooldowns(); }, + // Attributes stats: { hp : 1, // health @@ -32,7 +33,8 @@ new Class('Thing', { shots : 5 // max projectiles in the air at once }, - + // AI "Cooldowns" (max frequency of each action per sec) + ai : {}, // see Tank // *** Bookkeeping *** // @@ -111,14 +113,20 @@ new Class('Thing', { }, createCooldowns : function createCooldowns(){ - var cs = this.cooldowns = { - attack: new Cooldown(1000 * this.stats.speed) + this.cooldowns = { + 'attack': new Cooldown(1000 * this.stats.speed) }; - this._cooldowns = Y(cs); + this.ai = Y(this.ai).map(function(freq, k){ + return new Cooldown(1000 * freq); + }); + + this._cooldowns = Y(this.cooldowns); + this._ai = Y(this.ai); }, updateCooldowns : function updateCooldowns(){ this._cooldowns.invoke('update', NOW); + this._ai.invoke('update', NOW); return this; }, diff --git a/tags.html b/tags.html index 66120b7..6a71653 100644 --- a/tags.html +++ b/tags.html @@ -3,16 +3,16 @@ - + - + @@ -41,7 +41,7 @@ - + diff --git a/tanks.php b/tanks.php deleted file mode 100644 index 60ca22d..0000000 --- a/tanks.php +++ /dev/null @@ -1,132 +0,0 @@ - "src/Y/index.cjs", "loader" => "y-loader.js" ) - array( "main" => "src/tanks/index.cjs", "loader" => "tanks-loader.js" ) - ); - - - - static $mainScripts = array( - "src/tanks/ui/main.js" - ); - - static $srcScripts = array( - "src/tanks/tanks.js", - "src/tanks/globals.js", - "src/tanks/config.js", - "src/tanks/calc.js", - - "src/tanks/util/config.js", - - "src/tanks/thing/thing.js", - "src/tanks/thing/bullet.js", - "src/tanks/thing/tank.js", - "src/tanks/thing/customtank.js", - "src/tanks/thing/player.js", - - "src/tanks/map/loc.js", - "src/tanks/map/trajectory.js", - "src/tanks/map/level.js", - "src/tanks/map/pathmap.js", - - "src/tanks/ui/ui.js", - "src/tanks/ui/grid.js", - "src/tanks/ui/config.js", - - "src/tanks/game.js" - ); - - - - static $libScripts = array( - "lib/jquery-1.4.3.js", - "lib/jquery.sparkline.min.js", - "lib/jquery.hotkeys.js", - - // "lib/uki/uki.dev.js", - // "lib/uki/uki-theamless.dev.js", - // "lib/uki/uki-more.dev.js", - // "lib/uki/uki-theme/aristo.js", - - "src/future.js", - "lib/cjs/require.js", - - "src/Y/y.js.php", - "src/Y/modules/y.event.js", - "src/Y/modules/y.kv.js", - "src/Y/modules/y.cookies.js", - - "src/evt/evt.class.js", - - "src/ezl/math/math.js", - "src/ezl/math/vec.js", - "src/ezl/math/line.js", - - "src/ezl/layer.js", - "src/ezl/shape/shape.js", - "src/ezl/shape/circle.js", - "src/ezl/shape/rect.js", - "src/ezl/shape/line.js", - "src/ezl/shape/polygon.js", - - "src/ezl/util/binaryheap.js", - "src/ezl/util/graph.js", - "src/ezl/util/astar.js", - - "src/ezl/util/tree/quadtree.js", - - "src/ezl/loop/eventloop.js", - "src/ezl/loop/fps.js", - "src/ezl/loop/cooldown.js", - ); - - static function writeTags($scripts=null, $prefix="", $recompile=true) { - $scripts = $scripts ? $scripts : Tanks::SRC_AND_LIB; - - if ($scripts & Tanks::LIB_SCRIPTS) foreach (self::$libScripts as $s) js($s, $prefix); - if ($scripts & Tanks::SRC_SCRIPTS) foreach (self::$srcScripts as $s) js($s, $prefix); - if ($scripts & Tanks::MAIN_SCRIPTS) foreach (self::$mainScripts as $s) js($s, $prefix); - } - - static function compile($modules=null) { - $modules = ($modules ? $modules : self::$modules); - $PYTHONPATH = "PYTHONPATH='/Users/dsc/.python/lib/python:/usr/local/lib/python2.7/site-packages:/usr/local/lib/python2.6/site-packages'"; - error_reporting(E_ALL); - echo "\n"; - include "tags.html"; - } - - static function writeLoaders($modules=null, $recompile=true){ - $modules = ($modules ? $modules : self::$modules); - // js('lib/cjs/require.js'); - if ($recompile) Tanks::compile($modules); - // foreach ($modules as $module) js($module['loader'], Tanks::BUILD_DIR."/"); - } - -} - -function js($src, $prefix="") { - echo " \n"; -} - - - -?> - -- 1.7.0.4