game:
timeDilation : 1.0
gameoverDelay : 1000
- refSize : &ref_size 50
debug:
showFpsGraph : false
# createGridTable : false
# showGridCoords : false
+map:
+ refSize : &ref_size 50
+ minSplitSize : 12.5
+pathing:
+ pathSquare : *ref_size
+ overlayAiPaths : false
+ overlayPathmap : false
+ traceTrajectories : false
ui:
createGridCanvas : true
overlayOnPause : true
showAttackCooldown : false
showCountdown : true
-pathing:
- gridSquare : *ref_size
- overlayAiPaths : false
- overlayPathmap : false
- traceTrajectories : false
- type: wall
args: [300,350, 50,100]
- type: wall
- args: [50,350, 100,100]
- - type: wall
- args: [150,300, 50,50]
- - type: wall
args: [100,100, 50,50]
- type: fence
args: [360,210, 130,30]
args: [110,210, 30,30]
- type: fence
args: [210,210, 30,30]
+ - type: drywall
+ args: [50,350, 100,100]
+ - type: drywall
+ args: [150,300, 50,50]
units:
- type: player
align: 1
- loc: [325,475]
+ loc: [175,475]
- type: blue
align: 1
- loc: [175,475]
- - type: green
- align: 2
- loc: [25,375]
+ loc: [325,475]
+ # - type: green
+ # align: 2
+ # loc: [25,375]
- type: green
align: 2
loc: [75,25]
function(out, ch, i){
if ( !nameUnsafe && symbolPat.test(ch) )
return out;
- if ( upperPat.test(ch) )
+ if ( upperPat.test(ch) && i !== 0 )
out += '_';
return out + ch.toLowerCase();
}, '');
var Y = require('Y').Y
, Loc = require('ezl/loc/loc').Loc
-, math = require('ezl/math')
-, Vec = math.Vec
-, Line = math.Line
-, Rect = math.Rect
+, Rect = require('ezl/math/rect').Rect
, X1 = 0, Y1 = 1
, X2 = 2, Y2 = 3
-var Y = require('Y').Y
-, vec = require('ezl/math/vec')
-;
-
-Y(exports, {
- 'Vec' : vec.Vec,
- 'Line' : require('ezl/math/line').Line,
- 'Rect' : require('ezl/math/rect').Rect,
+//#exports clamp lerp
+require('Y').Y.core
+.extend(exports, {
'clamp' : function clamp(value, min, max) {
return Math.min(Math.max(value, min), max);
'lerp' : function lerp(x, a, b) {
return a + x*(b - a);
- },
-
- 'reflect' : function reflect(v, line){
- var dot = vec.dot
- , basev = vec.difference(v, line.p1);
- return line.vec()
- .scale(2 * dot(basev,line) / dot(line,line))
- .subtract(basev)
- .add(line.p1);
}
});
return this.calcY(x) === y;
},
+ intersection : function intersection(line){
+ var pt
+ , mA = this.slope, mB = line.slope
+ , xA = this.xint, xB = line.xint
+ , yA = this.yint, yB = line.yint
+ ;
+
+ if ( mA === mB ) return undefined;
+ if ( this.equals(line) ) return null;
+ if ( mA === 0 ) return line.pointAtY(yA);
+ if ( mB === 0 ) return this.pointAtY(yB);
+ if ( !isFinite(mA) ) return line.pointAtX(xA);
+ if ( !isFinite(mB) ) return this.pointAtX(xB);
+
+ var x = (yB - yA) / (mA - mB);
+ return this.pointAtX(x);
+ // var y = (xB - xA) * dSlope;
+ // return this.pointAtY(y);
+ },
+
isWithin : function isWithin(pt, w,h){
if ( !Y.isNumber(w) ){
h = w.height; w = w.width;
},
toString : function(){
- return '['+this.p1+', '+this.p2+', slope='+this.slope.toFixed(3)+']';
+ // return '['+this.p1+', '+this.p2+', slope='+this.slope.toFixed(3)+']';
+ return this.className+'(slope='+this.slope.toFixed(3)+', xint='+this.xint.toFixed(3)+', yint='+this.yint.toFixed(3)+')';
}
});
-var Y = require('Y').Y
+var Y = require('Y').Y
+, math = require('ezl/math')
+
, _X = 0, _Y = 1
,
});
-// Can't import from math due to deps
-function lerp(x, a, b) { return a + x*(b - a); }
-
-Y.extend(exports, {
+var vec = {
sum : function sum(a, b) {
return new Vec(a[_X]+b[_X], a[_Y]+b[_Y]);
},
},
lerp : function vecLerp(x, a, b) {
- return new Vec( lerp(x, a[_X], b[_X]),
- lerp(x, a[_Y], b[_Y]) );
+ return new Vec( math.lerp(x, a[_X], b[_X]),
+ math.lerp(x, a[_Y], b[_Y]) );
},
manhattan: function manhattan(x1,y1, x2,y2) {
var d1 = Math.abs(x2 - x1)
, d2 = Math.abs(y2 - y1) ;
return d1 + d2;
+ },
+
+ reflect : function reflect(v, line){
+ var basev = vec.difference(v, line.p1);
+ return line.vec()
+ .scale(2 * vec.dot(basev,line) / vec.dot(line,line))
+ .subtract(basev)
+ .add(line.p1);
}
-});
+};
+
+//#exports sum difference dot lerp manhattan reflect
+Y.extend(exports, vec);
+
//#ensure "jquery"
-
var Y = require('Y').Y
-, math = require('ezl/math')
+
+, line = require('ezl/math/line')
, Layer = require('ezl/layer/layer').Layer
, Shape = require('ezl/shape/shape').Shape
,
this.x1 = pos.left; this.x2 += this.x1;
this.y1 = pos.top; this.y2 += this.y1;
- this.line = new math.Line(this.x1,this.y1, this.x2,this.y2, (this.line||{}).tdist);
+ this.line = new line.Line(this.x1,this.y1, this.x2,this.y2, (this.line||{}).tdist);
return this;
},
, ensure = require('Y/types/object').ensure
, Config = require('Y/modules/y.config').Config
-, Vec = require('ezl/math').Vec
+, Vec = require('ezl/math/vec').Vec
, DataFile = require('ezl/util/data/datafile').DataFile
, Loader = require('ezl/util/data/loader').Loader
,
;
// Updates the midpoint square when square-size changes
-config.addEventListener('set:pathing.gridSquare', function(evt){
+config.addEventListener('set:pathing.pathSquare', function(evt){
var sq = evt.data.newval;
- config.set('pathing.gridSquareMid', new Vec(sq/2, sq/2));
+ config.set('pathing.pathSquareMid', new Vec(sq/2, sq/2));
});
-config.set('pathing.gridSquare', config.get('pathing.gridSquare'));
+config.set('pathing.pathSquare', config.get('pathing.pathSquare'));
/// Load Data Files ///
base : 0,
val : 0,
max : 0,
+ ratio : 1.0,
init : function initStat(base, val, max, isInteger){
this.base = base;
this.val = val !== undefined ? val : base;
this.max = max !== undefined ? max : base;
+ this.ratio = this.val / this.max;
this.integer = !!isInteger;
},
modify : function modify(dv){
var v = Math.min(this.max, this.val+dv);
this.val = (this.integer ? Math.round(v) : v);
+ this.ratio = this.val / this.max;
return this;
},
break;
}
this.val = (this.integer ? Math.round(v) : v);
+ this.ratio = this.val / this.max;
return this;
},
return animation;
},
- addThing : function addThing(unit, x,y){
- if (unit instanceof Event)
- unit = unit.trigger;
-
+ noteThing : function noteThing(unit){
unit.game = this;
-
- if (x !== undefined) {
- unit.position(x,y);
- unit.render( this.level );
- } else if ( unit.isRenderable ) {
- unit.render( this.level );
- }
-
- this.pathmap.addBlocker(unit);
+ if (unit.dead) return unit;
if ( unit.active && !this.byId[unit.__id__] ) {
this.byId[unit.__id__] = unit;
return unit;
},
+ addThing : function addThing(unit, x,y){
+ if (unit instanceof Event)
+ unit = unit.trigger;
+
+ if (unit.dead) return unit;
+
+ this.noteThing(unit);
+
+ if (x !== undefined) {
+ unit.position(x,y);
+ unit.render( this.level );
+ } else if ( unit.isRenderable ) {
+ unit.render( this.level );
+ }
+
+ this.pathmap.addBlocker(unit);
+
+ return unit;
+ },
+
killThing : function killThing(unit){
if (unit instanceof Event)
unit = unit.data.unit;
--- /dev/null
+var Y = require('Y').Y
+, clamp = require('ezl/math').clamp
+, Vec = require('ezl/math/vec').Vec
+
+, config = require('tanks/config').config
+, Wall = require('tanks/map/wall').Wall
+,
+
+
+Drywall =
+exports['Drywall'] =
+Wall.subclass('Drywall', {
+ minSplitSize : 25,
+
+ isReflective : false,
+ isBoundary : false,
+ isSplittable : true,
+
+ fillColors : [[139,49,20], [245,154,180], [255,246,174]],
+
+ fillStyle : 'rgba(249,190,0, 0.25)',
+ strokeStyle : 'rgba(249,190,0, 0.5)',
+ lineWidth : 1,
+
+ stats : {
+ hp : 3,
+ },
+
+
+ init : function initDrywall(x,y, w,h, hp){
+ this.stats = Y.extend({}, this.stats);
+ this.stats.hp = hp || this.stats.hp;
+
+ var line = Math.round(Math.min(w,h) * 0.2);
+ this.lineWidth = clamp(line, 1, 3);
+
+ Wall.init.call(this, x,y, w,h, this.isBoundary);
+ this.updateColor();
+ },
+
+ updateColor : function updateColor(){
+ var L = this.fillColors.length-1
+ , i = clamp(Math.floor(this.stats.hp.ratio*L), 0, L)
+ , color = this.fillColors[i]
+ ;
+ this.fillStyle = 'rgba('+color.join(',')+', 0.25)';
+ this.strokeStyle = 'rgba('+color.join(',')+', 0.50)';
+ console.log(this+'color = '+this.fillStyle+' ('+i+')');
+ return this;
+ },
+
+ dealDamage : function dealDamage(d, source){
+ Wall.fn.dealDamage.apply(this, arguments);
+ if (!this.dead) {
+ var old = this.fillStyle;
+ if (old !== this.updateColor().fillStyle)
+ this.render();
+ }
+ return this;
+ },
+
+ split : function split(gap){
+ if (!this.isSplittable || this.width <= this.minSplitSize)
+ return this;
+
+ var _x = this.loc.x, _y = this.loc.y
+ , mx = _x+this.width, my = _y+this.height
+ , size = this.width * 0.5
+ , gapX = gap ? gap.x : Infinity
+ , gapY = gap ? gap.y : Infinity
+ , hp = Math.max(Math.round(this.stats.hp.max * 0.5), 1.0)
+
+ , x, y, nextX, nextY, wall
+ ;
+
+ // console.group(this+'.split()');
+ // console.log('gap: ('+gapX+','+gapY+')');
+ // console.log('loc: '+this.loc);
+ // console.log('dim: w='+this.width+'; h='+this.height+'; size='+size);
+ // console.log('max: x='+mx+'; y='+my);
+ for (x = _x; x<mx; x=nextX) {
+ nextX = x + size;
+ for (y = _y; y<my; y=nextY){
+ nextY = y + size;
+ wall = new Drywall(x,y, size,size, hp);
+ if ( !(x <= gapX && gapX <= nextX && y <= gapY && gapY <= nextY) ) {
+ // console.log('Drywall('+x+','+y+', '+size+','+size+', '+hp+')');
+
+ } else {
+ // console.log('Gap! ('+x+' <= '+gapX+' <= '+nextX+') && ('+y+' <= '+gapY+' <= '+nextY+')');
+ wall.dead = true;
+ wall.split(gap).remove();
+ this.game.killThing(wall);
+ }
+ }
+ }
+ // console.groupEnd();
+ return this;
+ },
+
+ toString : Wall.fn.toString
+
+});
+
+Drywall.addEventListener('destroy', function(evt){
+ var d = evt.data
+ , killer = d.killer
+ , side = d.side
+ , gap = killer && side && side.intersection(killer.trajectory);
+ if (gap) evt.data.unit.split(gap);
+});
+config.updateOnChange('map.minSplitSize', Drywall.fn);
init : function initFence(x,y, w,h){
Wall.init.call(this, x,y, w,h, this.isBoundary);
- }
+ },
+
+ toString : Wall.fn.toString
});
-Wall.register('fence', Fence);
require('Y').Y
.extend(exports, {
- 'pathing' : require('tanks/map/pathing'),
+ 'pathing' : require('tanks/map/pathing'),
- 'Map' : require('tanks/map/map').Map,
- 'Level' : require('tanks/map/level').Level,
- 'Wall' : require('tanks/map/wall').Wall,
- 'Fence' : require('tanks/map/fence').Fence
+ 'Map' : require('tanks/map/map').Map,
+ 'Level' : require('tanks/map/level').Level,
+ 'Wall' : require('tanks/map/wall').Wall,
+ 'Drywall' : require('tanks/map/drywall').Drywall,
+ 'Fence' : require('tanks/map/fence').Fence
});
// -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
-var Y = require('Y').Y
+var Y = require('Y').Y
-, math = require('ezl/math')
, vec = require('ezl/math/vec')
+, Vec = vec.Vec
+, Line = require('ezl/math/line').Line
, QuadTree = require('ezl/util/tree/quadtree').QuadTree
, BinaryHeap = require('ezl/util/tree/binaryheap').BinaryHeap
-, Vec = vec.Vec
-, Line = math.Line
-, config = require('tanks/config').config
+, config = require('tanks/config').config
, constants = require('tanks/constants')
-, BoundsType = constants.BoundsType
+, BoundsType = constants.BoundsType
, DensityType = constants.DensityType
-, Map = require('tanks/map/map').Map
+, Map = require('tanks/map/map').Map
, SQRT_TWO = Math.sqrt(2)
exports['PathMap'] =
Map.subclass('PathMap', {
// Config
- gridSquare : null,
- gridSquareMid : null,
+ pathSquare : null,
+ pathSquareMid : null,
// State
_squares : null, // Cache of Square objects
},
/**
- * Takes normal (not gridSquare-relative) coordinates.
+ * Takes normal (not pathSquare-relative) coordinates.
*/
_getSquare : function getSquare(x,y){
if (x instanceof Array) { y=x[1]; x=x[0]; }
var cache = this._squares
- , SIZE = this.gridSquare ;
+ , SIZE = this.pathSquare ;
x = Math.floor(x / SIZE) * SIZE;
y = Math.floor(y / SIZE) * SIZE;
// End case -- result has been found, return the traced path
if (current === end) {
var path = Y([])
- , mid = this.gridSquareMid
+ , mid = this.pathSquareMid
, node = current;
while( node.prev ) {
path.push( vec.sum(node,mid) );
vec2Square : function vec2Square(x,y){
if (x instanceof Array){ y = x.y; x = x.x; }
- var floor = Math.floor, SIZE = this.gridSquare;
+ var floor = Math.floor, SIZE = this.pathSquare;
return new Vec(floor(x/SIZE), floor(y/SIZE));
},
square2Vec : function square2Vec(x,y){
if (x instanceof Array){ y = x.y; x = x.x; }
- var floor = Math.floor, SIZE = this.gridSquare;
+ var floor = Math.floor, SIZE = this.pathSquare;
return new Vec(floor(x)*SIZE, floor(y)*SIZE);
}
// , left = origin.x, right = bb.width - left
// , top = origin.y, bottom = bb.height - top
- , SIZE = pm.gridSquare
+ , SIZE = pm.pathSquare
, x = Math.floor(this.x)
, y = Math.floor(this.y)
// , x1 = x - left, x2 = x + SIZE + right
, abs = Math.abs
, pm = this.pathmap
, agent = pm._agent
- , SIZE = pm.gridSquare
+ , SIZE = pm.pathSquare
, x = this.x, y = this.y
, sq, cost, ix, iy, x1,y1, x2,y2
// Stay sync'd with config
config.updateOnChange(
- ['pathing.gridSquare', 'pathing.gridSquareMid'],
+ ['pathing.pathSquare', 'pathing.pathSquareMid'],
PathMap.fn);
var Y = require('Y').Y
, op = require('Y/op')
-, math = require('ezl/math')
-, Vec = math.Vec
-, Line = math.Line
-, Rect = math.Rect
+, Vec = require('ezl/math/vec').Vec
+, Line = require('ezl/math/line').Line
+, Rect = require('ezl/math/rect').Rect
, BoundingBox = require('ezl/loc').BoundingBox
, Thing = require('tanks/thing/thing').Thing
active : false,
createCooldowns : op.nop,
- // indestructable
- dealDamage : op.nop,
-
stats : {
hp : Infinity,
move : 0,
if (this.isBoundary)
return this;
- if (this.shape)
+ if (this.shape) {
+ parent = parent || this.shape.parent;
this.shape.remove();
+ }
+
+ if (!parent)
+ return this;
this.shape =
new Rect( this.renderWidth || this.width, this.renderHeight || this.height )
},
toString : function(){
- var bb = this.bbox
- , x1,y1, x2,y2;
+ var hp = (this.stats.hp.val !== Infinity ? ', hp='+this.stats.hp : '')
+ , bb = this.bbox
+ , x1,y1, x2,y2, p1;
if (bb){
+ p1 = bb.p1;
x1 = bb.x1; y1 = bb.y1;
x2 = bb.x2; y2 = bb.y2;
} else {
+ p1 = '(NaN,NaN)';
x1=y1=x2=y2=NaN;
}
- return this.className+'['+x1+','+y1+', '+x2+','+y2+'](w='+this.width+', h='+this.height+')';
+ return this.className+'('+bb.p1+', w='+this.width+', h='+this.height+', id='+this.__id__+hp+')';
}
});
Wall.register('wall', Wall);
+Wall.addEventListener('subclass',
+ function(evt){
+ var subcls = evt.data.child;
+ // console.log('Wall.register('+Y(subcls.className).camelToSnake()+', '+subcls.className+')');
+ Wall.register(Y(subcls.className).camelToSnake(), subcls);
+ });
+
// -*- mode: JavaScript; tab-width: 4; indent-tabs-mode: nil; -*-
-var Y = require('Y').Y
-, op = require('Y/op')
+var Y = require('Y').Y
+, op = require('Y/op')
-, math = require('ezl/math')
-, shape = require('ezl/shape')
-, Line = shape.Line
-, Circle = shape.Circle
+, math = require('ezl/math')
+, vec = require('ezl/math/vec')
+, shape = require('ezl/shape')
+, Line = shape.Line
+, Circle = shape.Circle
, config = require('tanks/config').config
, BoundsType = require('tanks/constants').BoundsType
if (!tvsl.side)
return console.error('Null reflection line!', 'to:', to, 'blocker:', unit, 'blockers:', tvsl.blockers._o);
- ng = math.reflect(traj.p2, tvsl.side);
+ ng = vec.reflect(traj.p2, tvsl.side);
traj.reset(to.x,to.y, ng.x,ng.y);
this.dirty = true; // to render the new reflection line, if it's visible
return;
}
- unit.dealDamage(this.stats.power.val, this);
+ unit.dealDamage(this.stats.power.val, this, tvsl.side);
this.destroy();
// Trigger explosion
// y = math.lerp(0.5,loc.y,uloc.y);
// }
- if ( unit.dead && asize >= bsize )
+ if ( unit.dead && !unit.isWall && asize >= bsize )
end = asize;
var dur = ex.timeBase + ex.timeInc*end;
init : function init(align){
- this.align = align || 0;
+ this.align = align || this.align;
this.createBoundingBox();
this.createStats();
this.createCooldowns();
return this;
},
- destroy : function destroy(){
+ destroy : function destroy(killer, side){
if (this.dead) return this;
this.dead = true;
- return this.remove().fire('destroy', this, { 'unit':this });
+ return this.remove().fire('destroy', this, { 'unit':this, 'killer':killer, 'side':side });
},
remove : function remove(){
return this;
},
- dealDamage : function dealDamage(d, source){
+ dealDamage : function dealDamage(d, source, side){
this.stats.hp.modify(-d);
if (this.stats.hp.val <= 0)
- this.destroy();
+ this.destroy(source, side);
return this;
},
exports['PathMapUI'] =
Rect.subclass('PathMapUI', {
// Config
- gridSquare : null,
+ pathSquare : null,
overlayPathmap : null,
overlayAiPaths : null,
pathWithUI : function pathWithUI(agent, x,y){
var pm = this.pathmap
, path = this._path.call(pm, agent, x,y)
- , start = vec.sum(pm._getSquare(agent.loc), pm.gridSquareMid)
+ , start = vec.sum(pm._getSquare(agent.loc), pm.pathSquareMid)
;
if (this.overlayAiPaths)
this.drawPath(agent, start, Y(path));
},
drawPathStep : function drawPathStep(ctx, p, idx){
- var SIZE = this.gridSquare
+ var SIZE = this.pathSquare
, off = SIZE*(idx === -1 ? 0.15 : 0.3)
, r = SIZE/2 - off
;
config.updateOnChange(
- ['pathing.gridSquare', 'pathing.overlayPathmap', 'pathing.overlayAiPaths'],
+ ['pathing.pathSquare', 'pathing.overlayPathmap', 'pathing.overlayAiPaths'],
PathMapUI.fn);
, c = drawPoint(v, null, '#552F74');
}
- var rv = math.reflect(c.vec,line)
+ var rv = vec.reflect(c.vec,line)
, rc = c.reflected = drawPoint(rv, null, '#F25522');
points.push(c);
return c;