From: dsc Date: Mon, 3 Jan 2011 09:09:50 +0000 (-0800) Subject: Fixes issue where mixin event would fire prior to members being added to a new class. X-Git-Url: http://git.less.ly:3516/?a=commitdiff_plain;h=1c6a99a756e331bb78f39dd107ecda18ab006887;p=tanks.git Fixes issue where mixin event would fire prior to members being added to a new class. --- diff --git a/src/Y/types/array.cjs b/src/Y/types/array.cjs index 9ffe267..90568f3 100644 --- a/src/Y/types/array.cjs +++ b/src/Y/types/array.cjs @@ -48,20 +48,21 @@ YCollection.subclass('YArray', function(YArray){ return this._o.length; }; + var self = this; Object.defineProperty(this, 'length', { 'get':size }); // Convenience getters for index 0-9 - // var thisget = op.thisget - // , thiskvset = op.thiskvset ; - // for (var i=0; i<10; i++) - // Object.defineProperty(this, i, { - // 'get' : thisget.partial(i), - // 'set' : thiskvset.partial(i) - // }); + for (var i=0; i<10; i++) + (function (j){ + Object.defineProperty(self, j+'', { + 'get' : function(){ return this._o[j]; }, + 'set' : function(v){ return (this._o[j] = v); }, + }); + }).call(this, i); this['toString'] = function(){ - return "Y[" + arrayToString.call(this._o) + "]"; + return "Y[" + arrayToString.call(this._o||[]) + "]"; }; /** diff --git a/src/Y/types/collection.cjs b/src/Y/types/collection.cjs index fcb4875..7fbced3 100644 --- a/src/Y/types/collection.cjs +++ b/src/Y/types/collection.cjs @@ -1,5 +1,5 @@ var YBase = require('Y/class').YBase -// , type = require('Y/type') +, type = require('Y/type') , core = require('Y/core') , del = require('Y/delegate') , op = require('Y/op') @@ -144,11 +144,10 @@ YBase.subclass('YCollection', { }); }, - // FIXME: del.map 'invoke' : function invoke(name){ var args = slice.call(arguments,1); return del.map(this, function(o){ - return o && o[name].apply(o, args); + return (o && type.isFunction(o[name])) ? o[name].apply(o, args) : null; }); }, diff --git a/src/evt.cjs b/src/evt.cjs index f5e84ba..fbce38a 100644 --- a/src/evt.cjs +++ b/src/evt.cjs @@ -46,11 +46,13 @@ var Y = require('Y').Y '__bases__', '__initialise__', '__class__', 'constructor', 'className', '__emitter__', '__static__', '__mixins__' ] //.concat(classStatics) -, mixinSkip = [ '__mixin_skip__', '__mixin_skip', 'onMixin', 'onInit', 'init' +, mixinSkip = [ '__mixin_skip__', 'onMixin', 'onInit', 'init' ].concat(classMagic) +, GLOBAL_ID = 0 , + /** * @private * Private delegating constructor. This function must be redefined for every @@ -72,6 +74,7 @@ function ConstructorTemplate() { // Not subclassing if ( unwrap(cls.caller) !== Y.fabricate ) { + instance.id = GLOBAL_ID++; // Perform actions that should only happen once in Constructor instance.__emitter__ = new Emitter(instance, cls); @@ -214,7 +217,7 @@ function Class(className, Parent, members){ // Fix Constructors NewClass.__super__ = SuperClass; // don't override NewClass.constructor -- it should be Function - NewClass.__bases__ = NewClass.fn.__bases__ = [SuperClass].concat( SuperClass.__bases__ || [ Class, Object ] ); + NewClass.__bases__ = NewClass.fn.__bases__ = Y([SuperClass]).concat( SuperClass.__bases__ || [ Class, Object ] ); prototype.constructor = prototype.__class__ = NewClass; NewClass.init = prototype.__initialise__ = createInitialise(NewClass); @@ -227,21 +230,6 @@ function Class(className, Parent, members){ // Record for metaprogramming KNOWN_CLASSES[className] = NewClass; - // 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) && classMagic.indexOf(k) === -1 ) - setDesc(prototype, k, getDesc(members,k)); - } - - // NewClass.__super__ = SuperClass; - // prototype.constructor = prototype.__class__ = NewClass; - } - var mixins = NewClass.__mixins__ = Y([]).concat(members.__mixins__||[], SuperClass.__mixins__||[]).unique() , statics = NewClass.__static__ = {} , pstatics = SuperClass.__static__ @@ -265,6 +253,25 @@ function Class(className, Parent, members){ setDesc(statics, k, desc); } + // Either invoke body constructor... + if ( isFunction(members) ) { + // body fn is responsible for calling mixin, attaching statics, etc + members.call(prototype, NewClass); + + // Or add new instance methods + } else { + for (var k in members) + if ( hasOwn.call(members,k) && classMagic.indexOf(k) === -1 ) + setDesc(prototype, k, getDesc(members,k)); + } + + // Notify mixins to let them finish up any customization + if (mixins.length) + mixins.forEach(function(mxn){ + if ( mxn && isFunction(mxn.fire) ) + mxn.fire('mixin', NewClass, { 'mixin':mxn, 'cls':NewClass }); + }); + // Notify parent of the subclass ParentEmitter.fire('subclass', NewClass, { @@ -284,7 +291,7 @@ Class.__super__ = Object; Class.__emitter__ = new Emitter(Class); Class.fn = Class.prototype; Class.fn.__class__ = Class.fn.constructor = Class; -Class.fn.__bases__ = Class.__bases__ = [ Object ]; +Class.fn.__bases__ = Class.__bases__ = Y([ Object ]); Class.className = Class.fn.className = "Class"; /* Class Methods */ @@ -333,11 +340,6 @@ new Class('Mixin', Class, { */ __mixin_skip__ : mixinSkip, - __mixin_skip : function __mixin_skip(k){ - var skip = this.__mixin_skip__; - return !hasOwn.call(this,k) || (skip && skip.indexOf(k) !== -1); - }, - __static__ : { mixInto : function mixIntoClass(cls){ var mxn = this; @@ -347,6 +349,10 @@ new Class('Mixin', Class, { }); +function mixinFilter(v, k){ + var skip = this.__mixin_skip__ || mixinSkip; + return hasOwn.call(this,k) && !(skip && skip.indexOf(k) !== -1); +} /** * Mixes a Mixin into another Class. @@ -368,7 +374,7 @@ function mixin(cls, _mxn){ , onInit = mproto.onInit ; - core.extend(proto, core.filter(mproto, mproto.__mixin_skip, mproto)); + core.extend(proto, core.filter(mproto, mixinFilter, mproto)); if (bases && bases.indexOf(mxn) === -1) bases.unshift(mxn); @@ -387,8 +393,8 @@ function mixin(cls, _mxn){ if ( hasOwn.call(mproto,'onInit') && isFunction(onInit) ) cls.addEventListener('init', onInit); - // Fire the mixin event on this Mixin - if ( isFunction(mxn.fire) ) + // Fire the mixin event on this Mixin only if we're done constructing the class + if ( isFunction(mxn.fire) && mixin.caller !== Class ) mxn.fire('mixin', cls, { 'mixin':mxn, 'cls':cls }); }); return cls; @@ -406,7 +412,7 @@ Mixin.addEventListener('subclass', if ( isFunction(onMixin) ) mxn.addEventListener('mixin', onMixin); - console.log('Mixin.subclass()', mxn, '<', d.parent, 'onMixin:', onMixin); + // console.log('Mixin.subclass()', mxn, '<', d.parent, 'onMixin:', onMixin); }); diff --git a/src/tanks/effects/buff.cjs b/src/tanks/effects/buff.cjs index 8baf546..99bc4bd 100644 --- a/src/tanks/effects/buff.cjs +++ b/src/tanks/effects/buff.cjs @@ -2,55 +2,85 @@ var Y = require('Y').Y , evt = require('evt') , mul = Y.op.curried.mul -, Speciated = require('tanks/mixins/speciated').Speciated -, Meronomic = require('tanks/mixins/meronomic').Meronomic +, Speciated = require('tanks/mixins/speciated').Speciated +, Meronomic = require('tanks/mixins/meronomic').Meronomic +, Quantified = require('tanks/mixins/quantified').Quantified , // Events: -// - live.{affect,stack} +// - live.affect // stacking will be done with copies and collapsed in the UI // - die.{expire,dismiss,dispel} Buff = exports['Buff'] = evt.subclass('Buff', { - __mixins__: [ Speciated, Meronomic ], // Configurable, Usable + __mixins__: [ Speciated, Meronomic, Quantified ], // Configurable, Usable /// Configurable /// - priority : 0, // Order of stat and effect application (lower is earlier) + priority : 0, // Order of stat and effect application (lower is earlier) // TODO stats : { - timeout : -1, - stacked : 1, - threat : 1 + timeout : Infinity, + threat : 1 // TODO }, - // {stat : Number|Function} Modifications to stats + // TODO: functions + // {stat : Number|Function} Modifications to stats stat_mods : { move : 2 }, - // effects : [], // {Function...} Effects to trigger on affect // XXX: not sure why i need this... - // triggers : {}, // {event : Effect} // maybe + effects : [], // {Function...} Effects to trigger on affect // XXX: not sure why i need this... + triggers : {}, // {event : Effect} // maybe /// Instance /// - owner : null, // Agent which originated the buff - target : null, // Agent to which the buff applies + owner : null, // Agent which originated the buff + target : null, // Agent to which the buff applies + game : null, + created : 0, - - init : function initBuff(owner, target){ + init : function initBuff(target, owner){ this.owner = owner; this.game = owner.game; this.target = target; + + this.createStats() + // this.createCooldowns(); + this.created = this.game.NOW; - // XXX: Speciated mixin must clone objects + this.expires = this.created + this.stats.timeout.val; + + this.applyStatMods(); + this.target.fire('buff.live.acquire', this, { 'buff':this, 'unit':target, 'owner':owner }); }, tick : function tick(elapsed, now){ + // this.updateCooldowns(); + if (now < this.expires) { + // this.remove(); + this.die('expire'); + } + + return this; + }, + + die : function die(reason){ + this.removeStatMods(); + this.target.fire('buff.die.'+reason, this, { 'buff':this, 'unit':target, 'owner':owner }); + return this; + }, + + dismiss : function dismiss(){ + return this.die('dismiss'); + }, + + dispel : function dispel(){ + return this.die('dispel'); }, applyStatMods : function applyStatMods(modifier){ - modifier = modifier || 1; + modifier = (modifier || 1); Y(this.stat_mods).map(mul(modifier)).forEach(this._applyStatMod, this); return this; }, @@ -58,18 +88,14 @@ evt.subclass('Buff', { _applyStatMod : function _applyStatMod(mod, name){ var keyparts = name.split('_') , key = keyparts[0] - , part = keyparts[1] || 'max' - ; + , part = keyparts[1] || 'max' ; this.target.stats[key].modifyPart(part, mod); - return this; }, removeStatMods : function removeStatMods(){ return this.applyStatMods(-1); } - // XXX: toString implemented by Informative - }) diff --git a/src/tanks/effects/index.cjs b/src/tanks/effects/index.cjs index e69de29..f21e3c8 100644 --- a/src/tanks/effects/index.cjs +++ b/src/tanks/effects/index.cjs @@ -0,0 +1,9 @@ +var stat = require('tanks/effects/stat'); + +require('Y').Y.core +.extend(exports, { + 'Buff' : require('tanks/effects/buff').Buff, + + 'Stat' : stat.Stat, + 'createStats' : stat.createStats +}); \ No newline at end of file diff --git a/src/tanks/index.js b/src/tanks/index.js index b5e13a5..d36bad7 100644 --- a/src/tanks/index.js +++ b/src/tanks/index.js @@ -9,12 +9,17 @@ require('tanks/globals'); // exports.tanks = tanks = { - 'config' : require('tanks/config').config, - 'ui' : require('tanks/ui'), - 'mixins' : require('tanks/mixins'), - 'Game' : require('tanks/game').Game, + 'config' : require('tanks/config').config, + 'constants' : require('tanks/constants'), + 'effects' : require('tanks/effects'), + 'fx' : require('tanks/fx'), + 'mixins' : require('tanks/mixins'), + 'map' : require('tanks/map'), + 'thing' : require('tanks/thing'), + 'ui' : require('tanks/ui'), - 'game' : null, + 'Game' : require('tanks/game').Game, + 'game' : null }; diff --git a/src/tanks/mixins/index.cjs b/src/tanks/mixins/index.cjs index da70058..e7384ef 100644 --- a/src/tanks/mixins/index.cjs +++ b/src/tanks/mixins/index.cjs @@ -1,4 +1,6 @@ -require('Y').Y.extend(exports, { - 'Speciated' : require('tanks/mixins/speciated').Speciated, - 'Meronomic' : require('tanks/mixins/meronomic').Meronomic +require('Y').Y.core +.extend(exports, { + 'Speciated' : require('tanks/mixins/speciated').Speciated, + 'Meronomic' : require('tanks/mixins/meronomic').Meronomic, + 'Quantified' : require('tanks/mixins/quantified').Quantified }); diff --git a/src/tanks/mixins/informative.cjs b/src/tanks/mixins/informative.cjs deleted file mode 100644 index d3e2268..0000000 --- a/src/tanks/mixins/informative.cjs +++ /dev/null @@ -1,19 +0,0 @@ -var Y = require('Y').Y -, Mixin = require('evt').Mixin -, - -Informative = -exports['Informative'] = -Mixin.subclass('Informative', { - - - onInit : function initInformative(evt){ - var instance = evt.data.instance; - - }, - - toString : function(){ - return this.className+'()'; - } - -}); diff --git a/src/tanks/mixins/quantified.cjs b/src/tanks/mixins/quantified.cjs new file mode 100644 index 0000000..26fdf98 --- /dev/null +++ b/src/tanks/mixins/quantified.cjs @@ -0,0 +1,78 @@ +var Y = require('Y').Y +, Mixin = require('evt').Mixin +, Cooldown = require('ezl/loop').Cooldown +, stat = require('tanks/effects/stat') +, + + +Quantified = +exports['Quantified'] = +Mixin.subclass('Quantified', { + + stats : {}, + cooldowns : {}, + ai : {}, + + + stat : function stat(k){ + var s = this.stats[k]; + return s ? s.val : s; + }, + + createStats : function createStats(){ + this.stats = stat.createStats(this.stats); + }, + + createCooldowns : function createCooldowns(){ + this._cooldowns = Y({ + 'attack': new Cooldown(1000 * this.stats.speed.val) + }); + this._ai = Y(this.ai).map(function(freq, k){ + return new Cooldown(1000 * freq); + }); + + this.cooldowns = this._cooldowns.end(); + this.ai = this._ai.end(); + }, + + updateCooldowns : function updateCooldowns(elapsed, now){ + this._cooldowns.invoke('tick', elapsed, now); + this._ai.invoke('tick', elapsed, now); + return this; + }, + + + // onInit : function initQuantified(evt){ + // var self = evt.data.instance; + // self.createStats(); + // self.createCooldowns(); + // }, + + onMixin : function onMixin(evt){ + var cls = evt.data.cls + , mxn = evt.data.mixin + , proto = cls.fn ; + console.log(mxn, '.onMixin()', cls); + proto.stats = cls.aggregate('stats'); + proto.cooldowns = cls.aggregate('cooldowns'); + proto.ai = cls.aggregate('ai'); + }, + + __static__ : { + + /** + * @return The merged pairs at key taken from all bases. + */ + aggregate : function aggregate(key){ + return this.__bases__ + .clone() + .unshift(this) + .reverse() + .reduce(function(acc, base){ + var proto = (base instanceof Function ? base.prototype : base); + return Y.extend(acc, proto[key]); + }, {}); + } + } + +}); diff --git a/src/tanks/mixins/speciated.cjs b/src/tanks/mixins/speciated.cjs index bb183cd..95af71e 100644 --- a/src/tanks/mixins/speciated.cjs +++ b/src/tanks/mixins/speciated.cjs @@ -5,34 +5,42 @@ var Y = require('Y').Y Speciated = exports['Speciated'] = Mixin.subclass('Speciated', { - - lol : 1, + name : "", + desc : "", + tags : [], __static__ : { - __known__ : null, + id2name : function id2name(id){ + var name = id+''; + // TODO: all YString methods to return YString :P But it'll proally break shit + name = Y(name).capitalize(); + name = Y(name).snakeToCamel(); + return name; + }, speciate : function speciate(id, props){ + if ( this.__known__[id] ) + throw new Error('A species of '+this.className+' already exists with the id '+id+'!'); + props = props || {}; props.__species__ = id; - delete props.id; + delete props.id; // reserved for instance id var cls = this - , speciesName = Y(Y(id+'').capitalize()).snakeToCamel() - , Species = cls.__known__[id] = cls.subclass(speciesName, props) + , speciesName = this.id2name(id) + , Species = this.__known__[id] = cls.subclass(speciesName, props) ; Species.__species_props__ = props; return Species; }, lookupSpecies : function lookupSpecies(id){ - var cls = this; - return cls.__known__[id]; + return this.__known__[id]; }, createSpeciesInstance : function createSpeciesInstance(id){ var args = Y(arguments,1) - , cls = this - , Species = cls.__known__[id]; + , Species = this.__known__[id]; return Species.instantiate.apply(Species, args); } @@ -41,7 +49,6 @@ Mixin.subclass('Speciated', { onMixin : function mixSpeciated(evt){ var cls = evt.data.cls; cls.__known__ = {}; - console.log('mixSpeciated()', 'cls:', cls, 'mxn:', evt.data.mixin); }, // onInit : function initSpeciated(evt){ diff --git a/src/tanks/thing/tank.cjs b/src/tanks/thing/tank.cjs index bc9199d..9a6094c 100644 --- a/src/tanks/thing/tank.cjs +++ b/src/tanks/thing/tank.cjs @@ -38,9 +38,8 @@ Thing.subclass('Tank', function(Tank){ // Attributes stats : { - hp : 2, // health + hp : 1, // health move : 0.75, // move speed (squares/sec) - rotate : HALF_PI, // rotation speed (radians/sec) power : 1, // attack power speed : 0.5, // attack cool (sec) shots : 4 // max projectiles in the air at once diff --git a/src/tanks/thing/thing.cjs b/src/tanks/thing/thing.cjs index ad36be2..d49794d 100644 --- a/src/tanks/thing/thing.cjs +++ b/src/tanks/thing/thing.cjs @@ -11,50 +11,33 @@ var Y = require('Y').Y , config = require('tanks/config').config , map = require('tanks/map/map') , stat = require('tanks/effects/stat') - -, THING_ID = 0 +, Quantified = require('tanks/mixins/quantified').Quantified , -fillStats = -exports['fillStats'] = -function fillStats(stats){ - return Y.reduce(stats, function(_stats, v, k){ - _stats[k] = v; - - k = Y(k); - var k_ = k.rtrim('_max') - , k_max = k_+'_max' - , k_base = k_+'_base' - ; - if ( _stats[k_] === undefined ) - _stats[k_] = v; - - if ( _stats[k_max] === undefined ) - _stats[k_max] = v; - - if ( _stats[k_base] === undefined ) - _stats[k_base] = _stats[k_max]; - - return _stats; - }, {}); -} -, - Thing = -exports['Thing'] = new evt.Class('Thing', { +exports['Thing'] = +new evt.Class('Thing', { + __mixins__ : [ Quantified ], + // Config showAttackCooldown : null, // Attributes stats: { + // rotate : HALF_PI, // rotation speed (radians/sec) [UNUSED] hp : 1, // health move : 1.0, // move speed (squares/sec) - rotate : HALF_PI, // rotation speed (radians/sec) [UNUSED] power : 1, // attack power speed : 0.5, // attack cool (sec) - shots : 5 // max projectiles in the air at once + shots : 5, // max projectiles in the air at once + sight : 5, // distance this unit can see (squares) + accuracy : 1.0 + }, + + cooldowns : { + attack: 'stats.speed' }, // AI "Cooldowns" (max frequency of each action per sec) @@ -100,7 +83,6 @@ exports['Thing'] = new evt.Class('Thing', { init : function init(align){ - this.id = THING_ID++; this.align = align || 0; this.createBoundingBox(); this.createStats(); @@ -112,34 +94,6 @@ exports['Thing'] = new evt.Class('Thing', { this.bbox = new BoundingBox(0,0, this.width,this.height, this.originX,this.originY); }, - stat : function stat(k){ - var s = this.stats[k]; - return s ? s.val : s; - }, - - createStats : function createStats(){ - this.stats = stat.createStats(this.stats); - }, - - createCooldowns : function createCooldowns(){ - this._cooldowns = Y({ - 'attack': new Cooldown(1000 * this.stats.speed.val) - }); - this._ai = Y(this.ai).map(function(freq, k){ - return new Cooldown(1000 * freq); - }); - - this.cooldowns = this._cooldowns.end(); - this.ai = this._ai.end(); - }, - - updateCooldowns : function updateCooldowns(elapsed, now){ - this._cooldowns.invoke('tick', elapsed, now); - this._ai.invoke('tick', elapsed, now); - return this; - }, - - position : function position(x,y){ if (x === undefined && y === undefined) return this.loc; diff --git a/www/deps.html b/www/deps.html index 6ebf6f0..df785cc 100644 --- a/www/deps.html +++ b/www/deps.html @@ -49,29 +49,32 @@ - - + - - + + - + + + + +