Adds Buff & Stat class
authordsc <david.schoonover@gmail.com>
Sat, 1 Jan 2011 03:29:59 +0000 (19:29 -0800)
committerdsc <david.schoonover@gmail.com>
Sat, 1 Jan 2011 03:29:59 +0000 (19:29 -0800)
26 files changed:
doc/notes.md
src/Y/class.cjs
src/Y/types/array.cjs
src/Y/types/string.cjs
src/Y/utils.cjs
src/ezl/layer/layer.cjs
src/tanks/constants.cjs [new file with mode: 0644]
src/tanks/effects/buff.cjs
src/tanks/effects/stat.cjs [new file with mode: 0644]
src/tanks/game.cjs
src/tanks/globals.js
src/tanks/map/map.cjs
src/tanks/map/pathmap.cjs
src/tanks/map/trajectory.cjs
src/tanks/map/traversal.cjs
src/tanks/mixins/configurable.cjs [copied from src/tanks/thing/zone.cjs with 100% similarity]
src/tanks/mixins/informative.cjs [copied from src/tanks/thing/zone.cjs with 100% similarity]
src/tanks/mixins/meronomic.cjs [copied from src/tanks/thing/zone.cjs with 100% similarity]
src/tanks/mixins/speciated.cjs [copied from src/tanks/thing/zone.cjs with 100% similarity]
src/tanks/mixins/usable.cjs [moved from src/tanks/thing/zone.cjs with 100% similarity]
src/tanks/thing/bullet.cjs
src/tanks/thing/tank.cjs
src/tanks/thing/thing.cjs
src/tanks/thing/wall.cjs
www/header.html
www/test/shapes/test-shapes.js

index 2c03c50..fc55836 100644 (file)
@@ -6,7 +6,6 @@
 - 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
index 73015eb..6848b76 100644 (file)
@@ -195,10 +195,33 @@ Class.fn.subclass = YFunction(subclass);
     });
 
 
+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
 });
 
index 8b6fc0a..0f2700c 100644 (file)
@@ -2,7 +2,7 @@ var Y           = require('Y/y').Y
 ,   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
@@ -19,15 +19,15 @@ YCollection.subclass('YArray', function(YArray){
     
     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'] });
     
     
index 4339f9a..5a82ff5 100644 (file)
@@ -1,5 +1,5 @@
 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')
@@ -10,9 +10,9 @@ exports['YString'] =
 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){
index a27df65..0f01984 100644 (file)
@@ -32,8 +32,8 @@ function bindAll(o, names){
 
 
 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.
@@ -51,10 +51,10 @@ defaults = {
     '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,
@@ -81,7 +81,7 @@ defaults = {
      */
     'fnFirst' : false
 };
-function mixin(options){
+function mixInto(options){
     var o = core.extend({}, defaults, options)
     ,   target = o.target
     ,   donor  = o.donor
@@ -123,4 +123,4 @@ function mixin(options){
 }
 
 exports['bindAll'] = bindAll;
-exports['mixin'] = mixin;
+exports['mixInto'] = mixInto;
index 75d5408..beaa2ac 100644 (file)
@@ -64,8 +64,8 @@ Y.subclass('Layer', {
         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,
@@ -172,7 +172,7 @@ Y.subclass('Layer', {
         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
@@ -204,7 +204,7 @@ Y.subclass('Layer', {
         
         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
@@ -230,7 +230,7 @@ Y.subclass('Layer', {
         
         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
@@ -279,7 +279,7 @@ Y.subclass('Layer', {
             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
diff --git a/src/tanks/constants.cjs b/src/tanks/constants.cjs
new file mode 100644 (file)
index 0000000..d6eba26
--- /dev/null
@@ -0,0 +1,39 @@
+//#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
index e69de29..84375bb 100644 (file)
@@ -0,0 +1,60 @@
+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?
+    
+})
+
+
+
diff --git a/src/tanks/effects/stat.cjs b/src/tanks/effects/stat.cjs
new file mode 100644 (file)
index 0000000..3da9b06
--- /dev/null
@@ -0,0 +1,127 @@
+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);
+};
index 3678225..b6d99ad 100644 (file)
@@ -86,11 +86,11 @@ Y.subclass('Game', {
         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);
@@ -103,7 +103,7 @@ Y.subclass('Game', {
         
         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 )
@@ -121,16 +121,16 @@ Y.subclass('Game', {
         return running;
     },
     
+    
+    
+    // *** Agent Management *** //
+    
     addAnimation : function addAnimation(animation){
         this.animations.push(animation);
         this.level.append(animation);
         return animation;
     },
     
-    
-    
-    // *** Agent Management *** //
-