- Config-driven unit-types (name, stats, properties; pointers to behavior scripts, assets)
- Game scoring
- Support touch events (for iPad?)
-- Migrate A* code into PathMap
# Code Notes
});
+var
+
+/**
+ * Everybody's favourite party metaclass!
+ */
+Mixin =
+exports['Mixin'] =
+Y.subclass('Mixin', {
+ __mixin_skip__ : [ 'mixIntoClass' ],
+
+
+
+ /**
+ * Mixes this Mixin into another Class.
+ */
+ mixIntoClass : function mixIntoClass(cls){
+
+ },
+
+})
+,
+
+
/**
* Root-class for all Y-objects.
*/
-var YBase = new Class("YBase", {
+YBase = new Class("YBase", {
__y__ : true
});
, del = require('Y/delegate')
, type = require('Y/type')
, op = require('Y/op')
-, mixin = require('Y/utils').mixin
+, mixInto = require('Y/utils').mixInto
, YCollection = require('Y/types/collection').YCollection
, _Array = Array
var newYArray = YArray.instantiate.bind(YArray);
- mixin({ target:YArray, donor:_Array, context:'_o',
+ mixInto({ target:YArray, donor:_Array, context:'_o',
names:'indexOf lastIndexOf shift pop join'.split(' ') });
- mixin({ target:YArray, donor:_Array, context:'_o', chain:true,
+ mixInto({ target:YArray, donor:_Array, context:'_o', chain:true,
names:'push unshift sort splice reverse'.split(' ') });
- mixin({ target:YArray, donor:_Array, context:'_o', fnFirst:true, wrap:newYArray,
+ mixInto({ target:YArray, donor:_Array, context:'_o', fnFirst:true, wrap:newYArray,
names:'map forEach filter'.split(' ') });
- mixin({ target:YArray, donor:_Array, context:'_o', fnFirst:true,
+ mixInto({ target:YArray, donor:_Array, context:'_o', fnFirst:true,
names:['reduce', 'some', 'every'] });
- mixin({ target:YArray, donor:_Array, context:'_o', wrap:newYArray,
+ mixInto({ target:YArray, donor:_Array, context:'_o', wrap:newYArray,
names:['slice'] });
var YCollection = require('Y/types/collection').YCollection
-, mixin = require('Y/utils').mixin
+, mixInto = require('Y/utils').mixInto
, op = require('Y/op')
, core = require('Y/core')
, del = require('Y/delegate')
YCollection.subclass('YString', function(YString){
var newYString = YString.instantiate.bind(YString);
- mixin({ target:YString, donor:String, context:'_o', wrap:newYString,
+ mixInto({ target:YString, donor:String, context:'_o', wrap:newYString,
names:'slice substr substring concat replace toLowerCase toUpperCase'.split(' ') });
- mixin({ target:YString, donor:String, context:'_o',
+ mixInto({ target:YString, donor:String, context:'_o',
names:'split indexOf lastIndexOf charAt charCodeAt'.split(' ') });
function trim(val){
var
-This = mixin['This'] = {},
-Target = mixin['Target'] = {},
+This = mixInto['This'] = {},
+Target = mixInto['Target'] = {},
defaults = {
/**
* {Object|Function} target Required. Target object onto which methods will be added. If this a Function, it's prototype will be used.
'names' : null,
/**
- * {mixin.This|mixin.Target|String} [context=mixin.This]
+ * {mixInto.This|mixInto.Target|String} [context=mixInto.This]
* Sets the call context for the function.
- * - {mixin.This}: (Default) Use runtime `this` as context.
- * - {mixin.Target}: Use `target` as context.
+ * - {mixInto.This}: (Default) Use runtime `this` as context.
+ * - {mixInto.Target}: Use `target` as context.
* - {String}: Names a property on `this` to use. If specified and property is false-y, `this` is used.
*/
'context' : This,
*/
'fnFirst' : false
};
-function mixin(options){
+function mixInto(options){
var o = core.extend({}, defaults, options)
, target = o.target
, donor = o.donor
}
exports['bindAll'] = bindAll;
-exports['mixin'] = mixin;
+exports['mixInto'] = mixInto;
this.negBleed = new Loc(0,0);
this.posBleed = new Loc(0,0);
- this.boundingBox = new BoundingBox(0,0, 0,0, this.originX,this.originY);
- this._origin = this.boundingBox.origin;
+ this.bbox = new BoundingBox(0,0, 0,0, this.originX,this.originY);
+ this._origin = this.bbox.origin;
this.transform = {
rotate : 0,
this.layerWidth = w;
this.layerHeight = h;
- var bb = this.boundingBox.resize(w,h)
+ var bb = this.bbox.resize(w,h)
, nb = this.negBleed, pb = this.posBleed
// HTMLCanvas.{width,height} is a long
this.layerWidth = w;
- var bb = this.boundingBox.resize(w, this.layerHeight)
+ var bb = this.bbox.resize(w, this.layerHeight)
// , ro = bb.relOrigin
, nb = this.negBleed
, cw = this.canvasWidth = Math.ceil(w + nb.x + this.posBleed.x); // HTMLCanvas.width is a long
this.layerHeight = h;
- var bb = this.boundingBox.resize(this.layerWidth, h)
+ var bb = this.bbox.resize(this.layerWidth, h)
// , ro = bb.relOrigin
, nb = this.negBleed
, ch = this.canvasHeight = Math.ceil(h + nb.y + this.posBleed.y); // HTMLCanvas.height is a long
x = ('left' in x ? x.left : x.x);
}
- var bbox = this.boundingBox.relocate(x,y);
+ var bbox = this.bbox.relocate(x,y);
this.css({
'left' : bbox.x1,
'top' : bbox.y1
--- /dev/null
+//#exports PathingType StatInvariant
+require('Y').Y.extend(exports, {
+
+ /// Pathing Constants ///
+ /** How this object interacts with the world. */
+ PathingType : {
+
+ /** Does not obstruct other objects. */
+ PASSABLE : 0,
+
+ /** Does not obstruct other objects, but still collides with them. */
+ ZONE : 1,
+
+ /** Obstructs other blockers with its BoundingBox. */
+ BLOCKING : 2,
+
+ /** Potentially obstructs other objects, but requires a special test once a BoundingBox collision has been detected. */
+ IRREGULAR : 3
+
+ },
+
+ /**
+ * Invariant to restore for the current value when modifying the base or max of a Stat.
+ * @see {tanks.effects.stat.Stat} for the default behavior of its methods.
+ */
+ StatInvariant : {
+
+ /** Do not change current value. */
+ NONE : 1,
+
+ /** Keep current value at the same ratio it was before the change. */
+ RATIO : 2,
+
+ /** Apply the full change to the current value. */
+ FULL : 3
+
+ }
+
+});
\ No newline at end of file
+var Y = require('Y').Y
+,
+
+
+// Events:
+// - live.{affect,stack}
+// - die.{expire,dismiss,dispel}
+Buff =
+exports['Buff'] =
+Y.subclass('Buff', {
+ // __mixins__: [ Informative, Speciated, Meronomic, Usable ], // Configurable?
+
+
+ /// Configurable ///
+ priority : 0, // Order of stat and effect application (lower is earlier)
+ stats : {
+ timeout : -1,
+ stacked : 1,
+ threat : 1
+ },
+ stat_mods : {}, // {stat : Number|Function} Modifications to stats
+ // triggers : {}, // {event : Effect} // maybe
+ // effects : [], // {Function...} Effects to trigger on affect // not sure why i need this
+
+ /// Instance ///
+ owner : null, // Agent which originated the buff
+ target : null, // Agent to which the buff applies
+
+
+
+ init : function initBuff(owner, target){
+ this.owner = owner;
+ this.game = owner.game;
+ this.target = target;
+ this.created = this.game.NOW;
+ // XXX: Speciated mixin must clone objects
+ },
+
+ applyStatMods : function applyStatMods(modifier){
+ modifier = modifier || 1;
+ Y(this.stat_mods).forEach(function(mod, name){
+ var keyparts = name.split('_')
+ , key = parts[0]
+ , part = parts[1] || 'val'
+ ;
+ this.target.stats[key].modifyPart(part, modifier * mod);
+ }, this);
+ return this;
+ },
+
+ removeStatMods : function removeStatMods(){
+ return this.applyStatMods(-1);
+ }
+
+ // XXX: toString implemented by Informative?
+
+})
+
+
+
--- /dev/null
+var Y = require('Y').Y
+
+, StatInvariant = require('tanks/constants').StatInvariant
+
+, NONE = StatInvariant.NONE
+, RATIO = StatInvariant.RATIO
+, FULL = StatInvariant.FULL
+,
+
+
+
+Stat =
+exports['Stat'] =
+Y.subclass('Stat', {
+ // __mixins__: [ Informative, Speciated, Meronomic, Usable ], // Configurable?
+
+ integer : true, // if true, current value is rounded after a change
+
+ base : 0,
+ val : 0,
+ max : 0,
+
+
+ init : function initStat(base, val, max, isFloat){
+ this.base = base;
+ this.val = val !== undefined ? val : base;
+ this.max = max !== undefined ? max : base;
+ this.integer = !isFloat;
+ },
+
+ clone : function clone(){
+ return new Stat(this.base, this.val, this.max, !this.integer);
+ },
+
+ /**
+ * Resets max value and current value to base value.
+ * @return {this}
+ */
+ reset : function reset(){
+ this.val = this.max = this.base;
+ return this;
+ },
+
+ /**
+ * @param {Number} dv Amount to add to current value.
+ * @return {this}
+ */
+ modify : function modify(dv){
+ var v = Math.min(this.max, this.val+dv);
+ this.val = (this.integer ? Math.round(v) : v);
+ return this;
+ },
+
+ /**
+ * @param {Number} dv Amount to add to base and max value.
+ * @param {StatInvariant} [inv=FULL] Invariant to restore for current value.
+ * @return {this}
+ */
+ modifyBase : function modifyBase(dv, inv){
+ this.base += dv;
+ return this.modifyMax(dv, inv || FULL);
+ },
+
+ /**
+ * @param {Number} dv Amount to add to current value.
+ * @param {StatInvariant} [inv=FULL] Invariant to restore for current value.
+ * @return {this}
+ */
+ modifyMax : function modifyMax(dv, inv){
+ var v = this.val
+ , oldmax = this.max;
+ this.max += dv;
+
+ switch ( inv ) {
+ case NONE: break;
+
+ case RATIO:
+ v = (v / oldmax) * this.max;
+ break;
+
+ case FULL:
+ default:
+ v += dv;
+ break;
+ }
+ this.val = (this.integer ? Math.round(v) : v);
+
+ return this;
+ },
+
+ modifyPart : function modifyPart(part, dv, inv){
+ var method =
+ (part === 'max' ? 'modifyMax' :
+ (part === 'base' ? 'modifyBase' : 'modify'));
+ return this[method](dv, inv);
+ },
+
+ toString : function(){
+ return this.val+'/'+this.max+(this.max !== this.base ? ' ['+this.base+']' : '');
+ }
+
+})
+;
+
+Stat.fn.modifyVal = Stat.fn.modify;
+
+
+Stat['from'] =
+function fromValue(v){
+ if ( v instanceof Stat )
+ return v.clone();
+ else if ( Y.isArray(v) )
+ return new Stat(v[0], v[1], v[2], v[3]);
+ else
+ return new Stat(v);
+};
+
+/**
+ * Accepts any number of objects which are combined to make the new map of names to stats.
+ */
+exports['createStats'] =
+function createStats(o){
+ var stats = Y({});
+ return stats
+ .extend.apply(stats, Y(arguments))
+ .map(Stat.from);
+};
var d = evt.data
, self = this;
- NOW = d.now;
- ELAPSED = d.elapsed;
- TICKS = d.ticks;
- SECONDTH = ELAPSED / 1000;
- SQUARETH = REF_SIZE * SECONDTH
+ NOW = this.NOW = d.now;
+ ELAPSED = this.ELAPSED = d.elapsed;
+ TICKS = this.TICKS = d.ticks;
+ SECONDTH = this.SECONDTH = ELAPSED / 1000;
+ SQUARETH = this.SQUARETH = REF_SIZE * SECONDTH
this.active.invoke('updateCooldowns', ELAPSED, NOW);
this.active.invoke('act', ELAPSED, NOW);
if ( this.player.dead )
this.gameover = 'lose';
- else if ( !this.units.filter('_.align !== 1 && !_.dead'.lambda()).size() )
+ else if ( !this.units.filter('_.align !== 1 && !_.dead').size() )
this.gameover = 'win';
if ( this.gameover )
return running;
},
+
+
+ // *** Agent Management *** //
+
addAnimation : function addAnimation(animation){
this.animations.push(animation);
this.level.append(animation);
return animation;
},
-
-
- // *** Agent Management *** //
-