var newYArray = YArray.instantiate.bind(YArray);
mixInto({ target:YArray, donor:_Array, context:'_o',
- names:'indexOf lastIndexOf shift pop join'.split(' ') });
+ names:'indexOf lastIndexOf shift join'.split(' ') });
mixInto({ target:YArray, donor:_Array, context:'_o', chain:true,
names:'push unshift sort splice reverse'.split(' ') });
mixInto({ target:YArray, donor:_Array, context:'_o', fnFirst:true, wrap:newYArray,
return this;
};
+ this['pop'] =
+ function pop(idx){
+ var r, A = this._o;
+ if (idx !== undefined) {
+ r = A[idx];
+ A.splice(idx, 1);
+ return r;
+ } else
+ return A.pop();
+ };
+
this['remove'] =
function remove(v){
var L = arguments.length;
'__bases__', '__initialise__', '__class__', 'constructor', 'className',
'__emitter__', '__static__', '__mixins__'
] //.concat(classStatics)
-, mixinSkip = [ '__mixin_skip__', 'onMixin', 'onInit', 'init'
+, mixinSkip = [ '__mixin_skip__', 'onMixin', 'onCreate', 'init'
].concat(classMagic)
, GLOBAL_ID = 0
,
*/
ConstructorTemplate =
exports['ConstructorTemplate'] =
-function ConstructorTemplate() {
- var k, v
+function ConstructorTemplate(_args) {
+ var instance = this
, cls = arguments.callee
- , instance = this
+ , caller = unwrap(cls.caller)
+ , args = (caller === instantiate) ? _args : arguments
;
// Not subclassing
- if ( unwrap(cls.caller) !== Y.fabricate ) {
+ if ( caller !== Y.fabricate ) {
instance.id = GLOBAL_ID++;
// Perform actions that should only happen once in Constructor
cls.fire('create', instance, {
'instance' : instance,
'cls' : cls,
- 'args' : Y(arguments)
+ 'args' : Y(args)
});
var initialise = instance.__initialise__;
if ( isFunction(initialise) )
- return initialise.apply(instance, arguments);
+ return initialise.apply(instance, args);
}
return instance;
setDesc(prototype, k, getDesc(members,k));
}
+ // prototype.__bind__ = Y([]).concat(prototype.__bind__||[], SuperClass.fn.__bind__||[]).unique();
+
// Notify mixins to let them finish up any customization
if (mixins.length)
mixins.forEach(function(mxn){
* Unlike the keyword `new`, instantiate can be applied.
*/
function instantiate(cls){
- var instance = cls.fabricate();
- if ( instance.initialise )
- instance.initialise.apply(instance, arguments);
- return instance;
+ return new cls(Y(arguments,1)); // the magic is done by checking in the constructor for this specific caller
}
var CST = Class.__static__ = {};
__mixin_skip__ : mixinSkip,
__static__ : {
+
+ /**
+ * Mixes this mixin into a class.
+ */
mixInto : function mixIntoClass(cls){
var mxn = this;
return mixin(cls, mxn);
+ },
+
+ /**
+ * @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]);
+ }, {});
}
+
}
});
*/
function mixin(cls, _mxn){
var proto = cls.fn
+ , bases = cls.__bases__
+ , cbinds = proto.__bind__
+ , cstatic = cls.__static__
, mxns = (Y.isArray(_mxn) ? _mxn.slice(0) : Y(arguments, 1))
;
mxns.reverse().forEach(function(mxn){
throw new Error('Cannot mix in non-object! '+mxn);
var mproto = (typeof mxn === "function") ? mxn.fn : mxn
- , bases = cls.__bases__
- , cstatic = cls.__static__
, statics = mxn.__static__
- , onInit = mproto.onInit
+ , onCreate = mproto.onCreate
+ , firstMix = (bases && bases.indexOf(mxn) === -1)
;
core.extend(proto, core.filter(mproto, mixinFilter, mproto));
- if (bases && bases.indexOf(mxn) === -1)
- bases.unshift(mxn);
-
// Add mixin statics to class constructor
if (statics){
for (var k in statics)
}
}
- // Register onInit to fire whenever a new instance is initialized
- if ( hasOwn.call(mproto,'onInit') && isFunction(onInit) )
- cls.addEventListener('init', onInit);
+ // Only perform these actions the first time we mix into this class
+ if (!firstMix)
+ return;
+
+ // Add to bases
+ bases.unshift(mxn);
+
+ // Register onCreate to fire whenever a new instance is initialized
+ if ( isFunction(cls.addEventListener) && hasOwn.call(mproto,'onCreate') && isFunction(onCreate) )
+ cls.addEventListener('create', onCreate);
// Fire the mixin event on this Mixin only if we're done constructing the class
if ( isFunction(mxn.fire) && mixin.caller !== Class )
// - die.{expire,dismiss,dispel}
Buff =
exports['Buff'] =
-evt.subclass('Buff', {
- __mixins__: [ Speciated, Meronomic, Quantified ], // Configurable, Usable
+new evt.Class('Buff', {
+ __mixins__: [ Speciated, Meronomic ], // Configurable, Usable
/// Configurable ///
priority : 0, // Order of stat and effect application (lower is earlier) // TODO
- stats : {
- timeout : Infinity,
- threat : 1 // TODO
- },
+ timeout : 5000,
+ threat : 1, // TODO
// TODO: functions
// {stat : Number|Function} Modifications to stats
triggers : {}, // {event : Effect} // maybe
/// Instance ///
+ name : 'Turrrrbo',
owner : null, // Agent which originated the buff
target : null, // Agent to which the buff applies
game : null,
init : function initBuff(target, owner){
- this.owner = owner;
- this.game = owner.game;
+ this.game = target.game;
this.target = target;
-
- this.createStats()
- // this.createCooldowns();
+ this.owner = owner || target;
this.created = this.game.NOW;
- this.expires = this.created + this.stats.timeout.val;
+ this.expires = this.created + this.timeout;
this.applyStatMods();
- this.target.fire('buff.live.acquire', this, { 'buff':this, 'unit':target, 'owner':owner });
+
+ var data = { 'buff':this, 'unit':this.target, 'owner':this.owner };
+ this.fire('buff.conjure', this.target, data);
+ this.target.fire('buff.conjure', this, data);
},
tick : function tick(elapsed, now){
- // this.updateCooldowns();
-
- if (now < this.expires) {
+ if (now >= this.expires) {
// this.remove();
this.die('expire');
}
die : function die(reason){
this.removeStatMods();
- this.target.fire('buff.die.'+reason, this, { 'buff':this, 'unit':target, 'owner':owner });
+ // var evt = 'buff.die.'+reason // We'll add this when we add polymorphic dispatchers
+ var evt = 'buff.die'
+ , data = { 'buff':this, 'unit':this.target, 'owner':this.owner };
+ this.fire(evt, this.target, data);
+ this.target.fire(evt, this, data);
return this;
},
exports['Configurable'] =
Mixin.subclass('Configurable', {
- onInit : function initConfigurable(evt){
+ onCreate : function initConfigurable(evt){
}
require('Y').Y.core
.extend(exports, {
- 'Speciated' : require('tanks/mixins/speciated').Speciated,
- 'Meronomic' : require('tanks/mixins/meronomic').Meronomic,
- 'Quantified' : require('tanks/mixins/quantified').Quantified
+ 'Speciated' : require('tanks/mixins/speciated').Speciated,
+ 'Meronomic' : require('tanks/mixins/meronomic').Meronomic,
+ 'Quantified' : require('tanks/mixins/quantified').Quantified,
+ 'Inventoried' : require('tanks/mixins/inventoried').Inventoried
});
--- /dev/null
+var Y = require('Y').Y
+, Mixin = require('evt').Mixin
+, Item = require('tanks/thing/item').Item
+,
+
+Inventoried =
+exports['Inventoried'] =
+Mixin.subclass('Inventoried', {
+ hasInventory : true,
+ inventory : [],
+
+
+ onCreate : function initInventoried(evt){
+ var self = evt.data.instance;
+ self.inventory = new Y.YArray();
+ },
+
+ // TODO: update UI; UI to inform item on activation
+ addItem : function addItem(item){
+ if (!item) return this;
+
+ this.inventory.push(item);
+
+ var data = { 'unit':this, 'item':item };
+ this.fire('item.acquire', item, data);
+ item.fire('item.acquire', this, data);
+
+ return this;
+ },
+
+ getItem : function getItem(idx){
+ this.inventory.attr(idx);
+ },
+
+ hasItem : function hasItem(item){
+ return this.inventory.has(item);
+ },
+
+ removeItem : function removeItem(idx){
+ if (idx instanceof Item){
+ var item = idx;
+ this.inventory.remove(item);
+ } else
+ var item = this.inventory.pop(idx);
+
+ if (item) {
+ var data = { 'unit':this, 'item':item };
+ this.fire('item.lose', item, data);
+ item.fire('item.lose', this, data);
+ }
+
+ return item;
+ },
+
+ dropItem : function dropItem(idx){
+ if (idx instanceof Item){
+ var item = idx;
+ this.inventory.remove(item);
+ } else
+ var item = this.inventory.pop(idx);
+
+ if (item) {
+ var data = { 'unit':this, 'item':item };
+ this.fire('item.lose.drop', item, data);
+ item.fire('item.lose.drop', this, data);
+ }
+
+ return item;
+ }
+
+
+});
exports['Meronomic'] =
Mixin.subclass('Meronomic', {
- onInit : function initMeronomic(evt){
+ onCreate : function initMeronomic(evt){
}
exports['Quantified'] =
Mixin.subclass('Quantified', {
- stats : {},
+ stats : {},
cooldowns : {},
- ai : {},
+ ai : {},
+ buffs : [],
+ onCreate : function initQuantified(evt){
+ var self = evt.data.instance;
+ self.buffs = Y([]);
+ self.addEventListener('buff.conjure', self.onBuffAcquired.bind(self));
+ self.addEventListener('buff.die', self.onBuffLost.bind(self));
+ },
+
stat : function stat(k){
var s = this.stats[k];
return s ? s.val : s;
updateCooldowns : function updateCooldowns(elapsed, now){
this._cooldowns.invoke('tick', elapsed, now);
this._ai.invoke('tick', elapsed, now);
+ this.buffs.invoke('tick', elapsed, now);
return this;
},
+ onBuffAcquired : function onBuffAcquired(evt){
+ var buff = evt.data.buff;
+ this.buffs.push(buff);
+ console.log('Buff '+buff+' acquired by '+this);
+ // TODO: update UI
+ },
- // onInit : function initQuantified(evt){
- // var self = evt.data.instance;
- // self.createStats();
- // self.createCooldowns();
- // },
+ onBuffLost : function onBuffLost(evt){
+ var buff = evt.data.buff;
+ this.buffs.remove(buff);
+ console.log('Buff '+buff+' lost by '+this);
+ // TODO: update UI
+ },
- onMixin : function onMixin(evt){
+ onMixin : function mixQuantified(evt){
var cls = evt.data.cls
, mxn = evt.data.mixin
, proto = cls.fn ;
- console.log(mxn, '.onMixin()', cls);
+ // 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]);
- }, {});
- }
}
});
cls.__known__ = {};
},
- // onInit : function initSpeciated(evt){
+ // onCreate : function initSpeciated(evt){
// var d = evt.data
// , instance = d.instance
// , Species = d.cls
, Rect = shape.Rect
, map = require('tanks/map/map')
+, Buff = require('tanks/effects/buff').Buff
, Thing = require('tanks/thing/thing').Thing
, ITEM_SIZE = REF_SIZE * 0.5
Item =
exports['Item'] =
Thing.subclass('Item', {
+ // __mixins__ : [ ],
+ __bind__ : [ 'onCollide', 'onAcquired', 'onLost' ],
+
align : 0, // 0 reserved for neutral units
blocking : map.ZONE,
name : 'Dummy', // {String} Display name
icon_inv : '', // {String} URL to inventory icon file (TODO)
icon_map : '', // {String} URL to map icon file (TODO)
- buffs : Y([]), // {Buff...} Passive effects (triggered on pickup)
- effects : Y([]), // {Function...} Active effects (triggered on activation)
- owner : null, // {Unit} Owner when picked up.
+ // {Buff...} Passive effects (triggered on pickup)
+ itemBuffs : Y([
+ Buff
+ ]),
+
+ // {Function...} Active effects (triggered on activation)
+ effects : Y([]),
+
+ // {Unit} Owner when picked up.
+ owner : null,
+ currentBuffs : null,
- // XXX: Setting these per-instance only while testing
- init : function initItem(name, buffs, effects){
+ init : function initItem(){
Thing.init.call(this, 0);
- if (name) this.name = name;
- if (buffs)
- this.buffs = Y(buffs);
- else
- this.buffs = this.buffs.clone();
+ this.itemBuffs = this.itemBuffs.clone();
+ this.effects = this.effects.clone();
- if (effects)
- this.effects = Y(effects);
- else
- this.effects = this.effects.clone();
-
- this.addEventListener('collide', this.onCollide.bind(this));
+ this.addEventListener('collide', this.onCollide);
+ this.addEventListener('item.acquire', this.onAcquired);
+ this.addEventListener('item.lose', this.onLost);
},
tick : function tick(elapsed, now){
-
+ // Buffs will take care of themselves as part of the unit
},
- activate : function activate(){
- if (!this.owner) return;
+ addToInventory : function addToInventory(unit){
+ if (!unit.hasInventory)
+ throw new Error('Unit has no inventory! '+unit);
+ unit.addItem(this);
+ return this;
},
- acquired : function acquired(unit){
- this.owner = unit;
- console.log(unit+' acquired '+this+'!');
- // TODO: apply buffs
- unit.fire('acquire', this, { 'unit':unit, 'item':this });
- // TODO: unit to listen, add to inventory, update UI, inform unit on activation
+ removeFromInventory : function removeFromInventory(){
+ if (this.owner)
+ this.owner.removeItem(this);
+ return this;
},
- dropped : function dropped(){
+ activate : function activate(){
if (!this.owner) return;
- var unit = this.owner;
- this.owner = null;
- console.log(unit+' dropped '+this+'!');
- unit.fire('drop', this, { 'unit':unit, 'item':this });
- // TODO: game to listen, re-add to level at unit.loc
+ },
+
+ onActivate : function onActivate(evt){
+ console.log(this+' activated!');
},
onCollide : function onCollide(evt){
var unit = evt.data.unit;
-
- if (unit.hasInventory) {
- this.destroy();
- this.acquired(unit);
- }
+ if (unit.hasInventory) unit.addItem(this);
+ },
+
+ onAcquired : function onAcquired(evt){
+ this.owner = evt.data.unit;
+ console.log(this.owner+' acquired '+this+'!');
+ this.currentBuffs = this.itemBuffs.invoke('instantiate', this.owner);
+ this.destroy(); // removes map object
+ },
+
+ onLost : function onLost(evt){
+ // if (!this.owner) return;
+ if ( this.currentBuffs )
+ this.currentBuffs.invoke('die', 'item.lost');
+ var unit = this.owner;
+ console.log(unit+' lost '+this+'!');
+ this.owner = null;
+ // TODO: game to listen, re-add to level at unit.loc
},
render : function render(parent){
var Y = require('Y').Y
, map = require('tanks/map/map')
, Tank = require('tanks/thing/tank').Tank
+, Inventoried = require('tanks/mixins/inventoried').Inventoried
,
PlayerTank =
exports['PlayerTank'] =
Tank.subclass('PlayerTank', {
+ __mixins__ : [ Inventoried ],
+
blocking : map.BLOCKING,
- hasInventory : true,
bodyColor : '#E73075',
turretColor : '#A72F5B',
<script src="build/ezl/shape/circle.js" type="text/javascript"></script>
<script src="build/ezl/shape.js" type="text/javascript"></script>
<script src="build/ezl.js" type="text/javascript"></script>
-<script src="build/jquery.hotkeys.js" type="text/javascript"></script>
<script src="build/tanks/globals.js" type="text/javascript"></script>
+<script src="build/jquery.hotkeys.js" type="text/javascript"></script>
<script src="build/Y/modules/y.kv.js" type="text/javascript"></script>
<script src="build/ezl/util/tree/binaryheap.js" type="text/javascript"></script>
<script src="build/tanks/constants.js" type="text/javascript"></script>
<script src="build/tanks/mixins/quantified.js" type="text/javascript"></script>
<script src="build/tanks/thing/thing.js" type="text/javascript"></script>
<script src="build/tanks/effects/buff.js" type="text/javascript"></script>
-<script src="build/tanks/mixins.js" type="text/javascript"></script>
<script src="build/tanks/map/trajectory.js" type="text/javascript"></script>
<script src="build/tanks/effects.js" type="text/javascript"></script>
+<script src="build/tanks/thing/item.js" type="text/javascript"></script>
<script src="build/tanks/ui/pathmapui.js" type="text/javascript"></script>
<script src="build/tanks/thing/wall.js" type="text/javascript"></script>
-<script src="build/tanks/thing/item.js" type="text/javascript"></script>
<script src="build/tanks/ui/grid.js" type="text/javascript"></script>
<script src="build/tanks/fx/explosion.js" type="text/javascript"></script>
<script src="build/tanks/thing/bullet.js" type="text/javascript"></script>
+<script src="build/tanks/mixins/inventoried.js" type="text/javascript"></script>
<script src="build/tanks/fx.js" type="text/javascript"></script>
<script src="build/tanks/thing/tank.js" type="text/javascript"></script>
+<script src="build/tanks/mixins.js" type="text/javascript"></script>
<script src="build/tanks/thing/player.js" type="text/javascript"></script>
<script src="build/tanks/map/level.js" type="text/javascript"></script>
<script src="build/tanks/thing.js" type="text/javascript"></script>