2016-05-07 03:00:25 -05:00
|
|
|
Level = function(levelName) {
|
2016-05-06 22:31:40 -05:00
|
|
|
var level = Scene();
|
|
|
|
|
2016-05-07 03:00:25 -05:00
|
|
|
var camera = {
|
|
|
|
x: 0,
|
|
|
|
y: 0,
|
|
|
|
z: 0,
|
2016-05-08 14:09:32 -05:00
|
|
|
angle: 0,
|
|
|
|
fwdX: 0,
|
2016-05-08 18:26:36 -05:00
|
|
|
fwdY: 0,
|
|
|
|
momx: 0,
|
|
|
|
momy: 0,
|
|
|
|
oldangle: 0
|
2016-05-07 03:00:25 -05:00
|
|
|
};
|
|
|
|
|
2016-05-07 10:30:18 -05:00
|
|
|
level.map = LevelDatabase[levelName];
|
|
|
|
|
2016-05-08 18:26:36 -05:00
|
|
|
// Scroll planes
|
|
|
|
var waterBack = ScrollPlane(0, 20, 1, 1, 0, 0, "waterback", 130);
|
|
|
|
var waves = [];
|
|
|
|
|
|
|
|
for (var i = 1024; i > 150; i *= 0.85) {
|
|
|
|
waves.push(ScrollPlane(
|
|
|
|
i, -3000/i, 40, 250/i, (i-420)/2, (Math.random()*300-150)/i, "water", Math.round(i/8)
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
2016-05-07 12:54:31 -05:00
|
|
|
// A set of blocks that entities are contained in.
|
2016-05-06 22:31:40 -05:00
|
|
|
// Blocks two grid spaces away (in a square, corners excluded) from the camera
|
|
|
|
// are thunk and rendered in a frame.
|
2016-05-07 03:00:25 -05:00
|
|
|
var entityGrid = {};
|
2016-05-06 22:31:40 -05:00
|
|
|
|
|
|
|
function getGridIndex(x, y) {
|
2016-05-07 12:54:31 -05:00
|
|
|
return Math.floor(x/600) + Math.floor(y/600)*512;
|
2016-05-06 22:31:40 -05:00
|
|
|
// If a map is bigger than 200k pixels one way then you have other problems.
|
|
|
|
}
|
|
|
|
|
2016-05-07 03:00:25 -05:00
|
|
|
var dolphin;
|
|
|
|
|
2016-05-06 22:31:40 -05:00
|
|
|
level.init = function() {
|
2016-05-08 18:26:36 -05:00
|
|
|
level.stage.addChild(waterBack.plane);
|
|
|
|
waves.forEach(function(p) {
|
|
|
|
level.stage.addChild(p.plane);
|
|
|
|
});
|
|
|
|
|
2016-05-07 10:30:18 -05:00
|
|
|
dolphin = Dolphin(level, level.map.spawn.axis, level.map.spawn.position, level.map.spawn.z);
|
2016-05-07 03:00:25 -05:00
|
|
|
placeEntityInGrid(dolphin);
|
2016-05-07 12:25:02 -05:00
|
|
|
|
|
|
|
level.map.rocks.forEach(function(rock) {
|
|
|
|
placeEntityInGrid(Rock(level, rock.x, rock.y, rock.z, rock.type));
|
|
|
|
});
|
2016-05-06 22:31:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
level.think = function() {
|
2016-05-07 03:00:25 -05:00
|
|
|
eachEntity(function(entity) {
|
|
|
|
if (entity.think) {
|
|
|
|
entity.think();
|
|
|
|
}
|
|
|
|
placeEntityInGrid(entity);
|
|
|
|
});
|
2016-05-07 10:30:18 -05:00
|
|
|
|
2016-05-08 14:14:24 -05:00
|
|
|
cameraThinker();
|
|
|
|
}
|
|
|
|
|
|
|
|
function cameraThinker() {
|
2016-05-07 10:30:18 -05:00
|
|
|
// Camera thinker.
|
2016-05-07 12:25:02 -05:00
|
|
|
var targetX, targetY;
|
|
|
|
|
2016-05-08 14:09:32 -05:00
|
|
|
camera.fwdX += (dolphin.momentum.x/40 - camera.fwdX) / 32;
|
|
|
|
camera.fwdY += (dolphin.momentum.y*6 - camera.fwdY) / 32;
|
|
|
|
|
2016-05-07 12:25:02 -05:00
|
|
|
targetX = dolphin.position.x - 250*Math.cos(dolphin.angle);
|
|
|
|
targetY = dolphin.position.y - 250*Math.sin(dolphin.angle);
|
|
|
|
|
2016-05-08 18:26:36 -05:00
|
|
|
camera.x += camera.momx = (targetX - camera.x) / 4;
|
|
|
|
camera.y += camera.momy = (targetY - camera.y) / 4;
|
2016-05-08 14:09:32 -05:00
|
|
|
camera.z = dolphin.position.z + camera.fwdY;
|
|
|
|
camera.angle = Math.atan2(dolphin.position.y - camera.y, dolphin.position.x - camera.x)
|
|
|
|
+ camera.fwdX;
|
2016-05-06 22:31:40 -05:00
|
|
|
}
|
|
|
|
|
2016-05-07 03:00:25 -05:00
|
|
|
level.render = function(frames) {
|
|
|
|
eachEntity(function(entity) {
|
|
|
|
if (!entity.activeSprite) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-05-07 12:25:02 -05:00
|
|
|
var sprite = entity.activeSprite;
|
|
|
|
|
|
|
|
sprite.visible = false;
|
|
|
|
|
2016-05-07 03:00:25 -05:00
|
|
|
// Start with the angle and distance for the entity.
|
|
|
|
var xdist, ydist,
|
|
|
|
angle, distance;
|
|
|
|
xdist = entity.position.x - camera.x;
|
|
|
|
ydist = entity.position.y - camera.y;
|
2016-05-07 12:54:31 -05:00
|
|
|
angle = (Math.atan2(ydist, xdist) - camera.angle + Math.PI * 3) % (2 * Math.PI) - Math.PI;
|
2016-05-07 03:00:25 -05:00
|
|
|
distance = Math.sqrt(ydist*ydist + xdist*xdist);
|
|
|
|
|
2016-05-07 12:28:58 -05:00
|
|
|
if (distance > 1024 || distance < 16 || Math.abs(angle) > Math.PI/3) {
|
2016-05-07 03:00:25 -05:00
|
|
|
// Not in the viewport.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-05-08 19:05:43 -05:00
|
|
|
if ((entity.position.z > 0) != (camera.z > 0)
|
|
|
|
&& Math.abs(camera.z) > 120 && distance > 300) {
|
2016-05-08 18:26:36 -05:00
|
|
|
// On the wrong side of the water.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-05-07 03:00:25 -05:00
|
|
|
// In the viewport.
|
2016-05-07 12:25:02 -05:00
|
|
|
sprite.visible = true;
|
2016-05-07 23:24:30 -05:00
|
|
|
sprite.alpha = Math.min(4 - (distance/256), 1);
|
2016-05-07 03:00:25 -05:00
|
|
|
|
|
|
|
distance *= Math.cos(angle);
|
|
|
|
|
|
|
|
// Get screen position and scale.
|
|
|
|
var scrX, scrY, scrScale;
|
2016-05-08 19:05:43 -05:00
|
|
|
scrX = (Math.tan(angle) * 250) + 250;
|
2016-05-07 03:00:25 -05:00
|
|
|
scrScale = 250/distance;
|
|
|
|
scrY = (entity.position.z - camera.z)*scrScale + 150;
|
|
|
|
|
|
|
|
// Set sprite position.
|
2016-05-07 12:25:02 -05:00
|
|
|
sprite.position.set(scrX, scrY);
|
|
|
|
sprite.scale.set(scrScale, scrScale);
|
|
|
|
|
|
|
|
// Use distance to sort.
|
|
|
|
sprite.ZINDEX = Math.round(distance/8);
|
|
|
|
|
|
|
|
var index = level.stage.getChildIndex(sprite);
|
|
|
|
try {
|
|
|
|
while (index > 0 && level.stage.getChildAt(index-1).ZINDEX < sprite.ZINDEX) {
|
|
|
|
index--;
|
|
|
|
}
|
|
|
|
while (level.stage.getChildAt(index+1).ZINDEX > sprite.ZINDEX) {
|
|
|
|
index++;
|
|
|
|
}
|
|
|
|
} catch (e) {}
|
|
|
|
level.stage.setChildIndex(sprite, index);
|
2016-05-07 03:00:25 -05:00
|
|
|
});
|
2016-05-08 18:26:36 -05:00
|
|
|
|
|
|
|
// Scroll planes
|
|
|
|
var xScroll = Math.sqrt(camera.momx*camera.momx + camera.momy*camera.momy)
|
|
|
|
* Math.sin(Math.atan2(camera.momy, camera.momx)-camera.angle);
|
|
|
|
|
|
|
|
var turn = camera.angle - camera.oldangle;
|
|
|
|
camera.oldangle = camera.angle;
|
|
|
|
turn = (turn + Math.PI * 3) % (2 * Math.PI) - Math.PI;
|
|
|
|
|
|
|
|
waves.forEach(function(p) {
|
|
|
|
p.plane.tilePosition.x -= (xScroll*p.moveFactor) + (turn*p.turnFactor) - p.driftFactor;
|
|
|
|
|
|
|
|
p.plane.position.y = 150 - camera.z*p.moveFactor + p.z;
|
|
|
|
});
|
|
|
|
|
|
|
|
waterBack.plane.position.y = Math.max(0, Math.min(150-(camera.z/4), 170-(camera.z*1.5)));
|
|
|
|
waterBack.plane.scale.y = 280 - waterBack.plane.position.y;
|
2016-05-06 22:31:40 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
level.end = function() {
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2016-05-07 12:54:40 -05:00
|
|
|
level.colliding = function(object, tag, excludes) {
|
|
|
|
excludes = excludes || [];
|
|
|
|
var xoffs = object.position.x % 600 > 300 ? 1 : -1;
|
|
|
|
var yoffs = object.position.y % 600 > 300 ? 512 : -512;
|
|
|
|
|
|
|
|
try {
|
|
|
|
([0, xoffs, yoffs, xoffs+yoffs]).forEach(function(pos) {
|
|
|
|
if (entityGrid[pos]) {
|
|
|
|
entityGrid[pos].forEach(function(target) {
|
|
|
|
if (!target.bbox || target.bbox.tag != tag) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Math.abs(target.position.x - object.position.x)
|
|
|
|
> target.bbox.x + object.bbox.x) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Math.abs(target.position.y - object.position.y)
|
|
|
|
> target.bbox.y + object.bbox.y) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Math.abs(target.position.z - object.position.z)
|
|
|
|
> target.bbox.z + object.bbox.z) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (excludes.indexOf(target) != -1) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
throw target;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
} catch (toucher) {
|
|
|
|
return toucher;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-05-07 03:00:25 -05:00
|
|
|
function placeEntityInGrid(entity) {
|
|
|
|
var grid;
|
|
|
|
var gridpos = getGridIndex(entity.position.x, entity.position.y);
|
|
|
|
if (entity.gridPosition != gridpos) {
|
|
|
|
// Remove from old.
|
|
|
|
if (entity.gridPosition !== undefined) {
|
|
|
|
grid = entityGrid[entity.gridPosition];
|
|
|
|
var pos = grid.indexOf(entity);
|
|
|
|
grid.splice(pos, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add to new.
|
|
|
|
grid = (entityGrid[gridpos] || (entityGrid[gridpos] = []));
|
|
|
|
entity.gridPosition = gridpos;
|
|
|
|
grid.push(entity);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function eachEntity(callback) {
|
|
|
|
const offsets = [
|
|
|
|
-1025, -1024, -1023,
|
|
|
|
-514, -513, -512, -511, -510,
|
|
|
|
-2, -1, 0, 1, 2,
|
|
|
|
510, 511, 512, 513, 514,
|
|
|
|
1023, 1024, 1025
|
|
|
|
];
|
2016-05-07 12:54:31 -05:00
|
|
|
var pos = getGridIndex(camera.x, camera.y);
|
2016-05-07 03:00:25 -05:00
|
|
|
|
|
|
|
// Build up an array separately so that we can modify the entity arrays during the callback.
|
|
|
|
var objs = [];
|
|
|
|
offsets.forEach(function(offset) {
|
|
|
|
if (entityGrid[pos+offset]) {
|
|
|
|
entityGrid[pos+offset].forEach(function(entity) {
|
|
|
|
objs.push(entity);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
objs.forEach(callback);
|
|
|
|
}
|
|
|
|
|
2016-05-06 22:31:40 -05:00
|
|
|
return level;
|
|
|
|
}
|