274 lines
No EOL
8 KiB
JavaScript
Executable file
274 lines
No EOL
8 KiB
JavaScript
Executable file
ig.module(
|
|
'impact.collision-map'
|
|
)
|
|
.requires(
|
|
'impact.map'
|
|
)
|
|
.defines(function(){ "use strict";
|
|
|
|
ig.CollisionMap = ig.Map.extend({
|
|
|
|
lastSlope: 1,
|
|
tiledef: null,
|
|
|
|
init: function( tilesize, data, tiledef ) {
|
|
this.parent( tilesize, data );
|
|
this.tiledef = tiledef || ig.CollisionMap.defaultTileDef;
|
|
|
|
for( var t in this.tiledef ) {
|
|
if( t|0 > this.lastSlope ) {
|
|
this.lastSlope = t|0;
|
|
}
|
|
}
|
|
},
|
|
|
|
|
|
trace: function( x, y, vx, vy, objectWidth, objectHeight ) {
|
|
// Set up the trace-result
|
|
var res = {
|
|
collision: {x: false, y: false, slope: false},
|
|
pos: {x: x, y: y},
|
|
tile: {x: 0, y: 0}
|
|
};
|
|
|
|
// Break the trace down into smaller steps if necessary
|
|
var steps = Math.ceil(Math.max(Math.abs(vx), Math.abs(vy)) / this.tilesize);
|
|
if( steps > 1 ) {
|
|
var sx = vx / steps;
|
|
var sy = vy / steps;
|
|
|
|
for( var i = 0; i < steps && (sx || sy); i++ ) {
|
|
this._traceStep( res, x, y, sx, sy, objectWidth, objectHeight, vx, vy, i );
|
|
|
|
x = res.pos.x;
|
|
y = res.pos.y;
|
|
if( res.collision.x ) { sx = 0; vx = 0; }
|
|
if( res.collision.y ) { sy = 0; vy = 0; }
|
|
if( res.collision.slope ) { break; }
|
|
}
|
|
}
|
|
|
|
// Just one step
|
|
else {
|
|
this._traceStep( res, x, y, vx, vy, objectWidth, objectHeight, vx, vy, 0 );
|
|
}
|
|
|
|
return res;
|
|
},
|
|
|
|
|
|
_traceStep: function( res, x, y, vx, vy, width, height, rvx, rvy, step ) {
|
|
|
|
res.pos.x += vx;
|
|
res.pos.y += vy;
|
|
|
|
var t = 0;
|
|
|
|
// Horizontal collision (walls)
|
|
if( vx ) {
|
|
var pxOffsetX = (vx > 0 ? width : 0);
|
|
var tileOffsetX = (vx < 0 ? this.tilesize : 0);
|
|
|
|
var firstTileY = Math.max( Math.floor(y / this.tilesize), 0 );
|
|
var lastTileY = Math.min( Math.ceil((y + height) / this.tilesize), this.height );
|
|
var tileX = Math.floor( (res.pos.x + pxOffsetX) / this.tilesize );
|
|
|
|
// We need to test the new tile position as well as the current one, as we
|
|
// could still collide with the current tile if it's a line def.
|
|
// We can skip this test if this is not the first step or the new tile position
|
|
// is the same as the current one.
|
|
var prevTileX = Math.floor( (x + pxOffsetX) / this.tilesize );
|
|
if( step > 0 || tileX == prevTileX || prevTileX < 0 || prevTileX >= this.width ) {
|
|
prevTileX = -1;
|
|
}
|
|
|
|
// Still inside this collision map?
|
|
if( tileX >= 0 && tileX < this.width ) {
|
|
for( var tileY = firstTileY; tileY < lastTileY; tileY++ ) {
|
|
if( prevTileX != -1 ) {
|
|
t = this.data[tileY][prevTileX];
|
|
if(
|
|
t > 1 && t <= this.lastSlope &&
|
|
this._checkTileDef(res, t, x, y, rvx, rvy, width, height, prevTileX, tileY)
|
|
) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
t = this.data[tileY][tileX];
|
|
if(
|
|
t == 1 || t > this.lastSlope || // fully solid tile?
|
|
(t > 1 && this._checkTileDef(res, t, x, y, rvx, rvy, width, height, tileX, tileY)) // slope?
|
|
) {
|
|
if( t > 1 && t <= this.lastSlope && res.collision.slope ) {
|
|
break;
|
|
}
|
|
|
|
// full tile collision!
|
|
res.collision.x = true;
|
|
res.tile.x = t;
|
|
x = res.pos.x = tileX * this.tilesize - pxOffsetX + tileOffsetX;
|
|
rvx = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Vertical collision (floor, ceiling)
|
|
if( vy ) {
|
|
var pxOffsetY = (vy > 0 ? height : 0);
|
|
var tileOffsetY = (vy < 0 ? this.tilesize : 0);
|
|
|
|
var firstTileX = Math.max( Math.floor(res.pos.x / this.tilesize), 0 );
|
|
var lastTileX = Math.min( Math.ceil((res.pos.x + width) / this.tilesize), this.width );
|
|
var tileY = Math.floor( (res.pos.y + pxOffsetY) / this.tilesize );
|
|
|
|
var prevTileY = Math.floor( (y + pxOffsetY) / this.tilesize );
|
|
if( step > 0 || tileY == prevTileY || prevTileY < 0 || prevTileY >= this.height ) {
|
|
prevTileY = -1;
|
|
}
|
|
|
|
// Still inside this collision map?
|
|
if( tileY >= 0 && tileY < this.height ) {
|
|
for( var tileX = firstTileX; tileX < lastTileX; tileX++ ) {
|
|
if( prevTileY != -1 ) {
|
|
t = this.data[prevTileY][tileX];
|
|
if(
|
|
t > 1 && t <= this.lastSlope &&
|
|
this._checkTileDef(res, t, x, y, rvx, rvy, width, height, tileX, prevTileY) ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
t = this.data[tileY][tileX];
|
|
if(
|
|
t == 1 || t > this.lastSlope || // fully solid tile?
|
|
(t > 1 && this._checkTileDef(res, t, x, y, rvx, rvy, width, height, tileX, tileY)) // slope?
|
|
) {
|
|
if( t > 1 && t <= this.lastSlope && res.collision.slope ) {
|
|
break;
|
|
}
|
|
|
|
// full tile collision!
|
|
res.collision.y = true;
|
|
res.tile.y = t;
|
|
res.pos.y = tileY * this.tilesize - pxOffsetY + tileOffsetY;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// res is changed in place, nothing to return
|
|
},
|
|
|
|
|
|
_checkTileDef: function( res, t, x, y, vx, vy, width, height, tileX, tileY ) {
|
|
var def = this.tiledef[t];
|
|
if( !def ) { return false; }
|
|
|
|
var lx = (tileX + def[0]) * this.tilesize,
|
|
ly = (tileY + def[1]) * this.tilesize,
|
|
lvx = (def[2] - def[0]) * this.tilesize,
|
|
lvy = (def[3] - def[1]) * this.tilesize,
|
|
solid = def[4];
|
|
|
|
// Find the box corner to test, relative to the line
|
|
var tx = x + vx + (lvy < 0 ? width : 0) - lx,
|
|
ty = y + vy + (lvx > 0 ? height : 0) - ly;
|
|
|
|
// Is the box corner behind the line?
|
|
if( lvx * ty - lvy * tx > 0 ) {
|
|
|
|
// Lines are only solid from one side - find the dot product of
|
|
// line normal and movement vector and dismiss if wrong side
|
|
if( vx * -lvy + vy * lvx < 0 ) {
|
|
return solid;
|
|
}
|
|
|
|
// Find the line normal
|
|
var length = Math.sqrt(lvx * lvx + lvy * lvy);
|
|
var nx = lvy/length,
|
|
ny = -lvx/length;
|
|
|
|
// Project out of the line
|
|
var proj = tx * nx + ty * ny;
|
|
var px = nx * proj,
|
|
py = ny * proj;
|
|
|
|
// If we project further out than we moved in, then this is a full
|
|
// tile collision for solid tiles.
|
|
// For non-solid tiles, make sure we were in front of the line.
|
|
if( px*px+py*py >= vx*vx+vy*vy ) {
|
|
return solid || (lvx * (ty-vy) - lvy * (tx-vx) < 0.5);
|
|
}
|
|
|
|
res.pos.x = x + vx - px;
|
|
res.pos.y = y + vy - py;
|
|
res.collision.slope = {x: lvx, y: lvy, nx: nx, ny: ny};
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
});
|
|
|
|
|
|
// Default Slope Tile definition. Each tile is defined by an array of 5 vars:
|
|
// - 4 for the line in tile coordinates (0 -- 1)
|
|
// - 1 specifing whether the tile is 'filled' behind the line or not
|
|
// [ x1, y1, x2, y2, solid ]
|
|
|
|
// Defining 'half', 'one third' and 'two thirds' as vars makes it a bit
|
|
// easier to read... I hope.
|
|
var H = 1/2,
|
|
N = 1/3,
|
|
M = 2/3,
|
|
SOLID = true,
|
|
NON_SOLID = false;
|
|
|
|
ig.CollisionMap.defaultTileDef = {
|
|
/* 15 NE */ 5: [0,1, 1,M, SOLID], 6: [0,M, 1,N, SOLID], 7: [0,N, 1,0, SOLID],
|
|
/* 22 NE */ 3: [0,1, 1,H, SOLID], 4: [0,H, 1,0, SOLID],
|
|
/* 45 NE */ 2: [0,1, 1,0, SOLID],
|
|
/* 67 NE */ 10: [H,1, 1,0, SOLID], 21: [0,1, H,0, SOLID],
|
|
/* 75 NE */ 32: [M,1, 1,0, SOLID], 43: [N,1, M,0, SOLID], 54: [0,1, N,0, SOLID],
|
|
|
|
/* 15 SE */ 27: [0,0, 1,N, SOLID], 28: [0,N, 1,M, SOLID], 29: [0,M, 1,1, SOLID],
|
|
/* 22 SE */ 25: [0,0, 1,H, SOLID], 26: [0,H, 1,1, SOLID],
|
|
/* 45 SE */ 24: [0,0, 1,1, SOLID],
|
|
/* 67 SE */ 11: [0,0, H,1, SOLID], 22: [H,0, 1,1, SOLID],
|
|
/* 75 SE */ 33: [0,0, N,1, SOLID], 44: [N,0, M,1, SOLID], 55: [M,0, 1,1, SOLID],
|
|
|
|
/* 15 NW */ 16: [1,N, 0,0, SOLID], 17: [1,M, 0,N, SOLID], 18: [1,1, 0,M, SOLID],
|
|
/* 22 NW */ 14: [1,H, 0,0, SOLID], 15: [1,1, 0,H, SOLID],
|
|
/* 45 NW */ 13: [1,1, 0,0, SOLID],
|
|
/* 67 NW */ 8: [H,1, 0,0, SOLID], 19: [1,1, H,0, SOLID],
|
|
/* 75 NW */ 30: [N,1, 0,0, SOLID], 41: [M,1, N,0, SOLID], 52: [1,1, M,0, SOLID],
|
|
|
|
/* 15 SW */ 38: [1,M, 0,1, SOLID], 39: [1,N, 0,M, SOLID], 40: [1,0, 0,N, SOLID],
|
|
/* 22 SW */ 36: [1,H, 0,1, SOLID], 37: [1,0, 0,H, SOLID],
|
|
/* 45 SW */ 35: [1,0, 0,1, SOLID],
|
|
/* 67 SW */ 9: [1,0, H,1, SOLID], 20: [H,0, 0,1, SOLID],
|
|
/* 75 SW */ 31: [1,0, M,1, SOLID], 42: [M,0, N,1, SOLID], 53: [N,0, 0,1, SOLID],
|
|
|
|
/* Go N */ 12: [0,0, 1,0, NON_SOLID],
|
|
/* Go S */ 23: [1,1, 0,1, NON_SOLID],
|
|
/* Go E */ 34: [1,0, 1,1, NON_SOLID],
|
|
/* Go W */ 45: [0,1, 0,0, NON_SOLID]
|
|
|
|
// Now that was fun!
|
|
};
|
|
|
|
|
|
// Static Dummy CollisionMap; never collides
|
|
ig.CollisionMap.staticNoCollision = { trace: function( x, y, vx, vy ) {
|
|
return {
|
|
collision: {x: false, y: false, slope: false},
|
|
pos: {x: x+vx, y: y+vy},
|
|
tile: {x: 0, y: 0}
|
|
};
|
|
}};
|
|
|
|
}); |