
Added more to base of new physics with level, removed orange square, added new particle, added enemy actor, added mouse detection, added health and coordinate info at bottom, changed sprites to simpler look with different colours, added reflection fade away, made acceleration and deceleration higher for better control, and added a reticule for the mouse.
578 lines
No EOL
16 KiB
JavaScript
578 lines
No EOL
16 KiB
JavaScript
window.onload = function(){
|
|
canvas = document.getElementById('game');
|
|
context = canvas.getContext('2d');
|
|
imagesLoading = 0;
|
|
reset();
|
|
};
|
|
|
|
window.requestAnimFrame = (function(){
|
|
return window.requestAnimationFrame ||
|
|
window.webkitRequestAnimationFrame ||
|
|
window.mozRequestAnimationFrame ||
|
|
window.oRequestAnimationFrame ||
|
|
window.msRequestAnimationFrame ||
|
|
function(callback, element){
|
|
window.setTimeout(callback, 1000 / 60);
|
|
};
|
|
})();
|
|
|
|
// Get mouse position
|
|
function getMouse(evt) {
|
|
var rect = canvas.getBoundingClientRect();
|
|
return {
|
|
x: evt.clientX - rect.left,
|
|
y: evt.clientY - rect.top,
|
|
click: false
|
|
};
|
|
}
|
|
|
|
// Set up variables
|
|
function reset(){
|
|
// Create arrays
|
|
actors = [];
|
|
controllers = [];
|
|
controllers = [];
|
|
particles = [];
|
|
ais = [];
|
|
keys = [];
|
|
mouse = {
|
|
x: 0,
|
|
y: 0,
|
|
down: false
|
|
};
|
|
sound = {
|
|
shoot1: new Audio('sfx.wav')
|
|
}
|
|
game = 'menu';
|
|
|
|
// Create 2 actors
|
|
actors[0] = new Actor(0, 'player', 200, 3, 5, 5);
|
|
//actors[1] = new Actor(1, 'player', 200, 3, 50, 5);
|
|
|
|
actors[2] = new Actor(6, 'all', 50, 3, 100, 150);
|
|
|
|
// Create player key controllers.
|
|
controllers[0] = new Controller(actors[0], [[68, 'moveRight'], [65, 'moveLeft'], [87, 'jump'], [67, 'camera'], [77, 'dark', 100], [83, 'shoot']]);
|
|
//controllers[1] = new Controller(actors[1], [[39, 'moveRight'], [37, 'moveLeft'], [38, 'jump'], [88, 'camera'], [78, 'bounce', 100]]);
|
|
|
|
ais[0] = new Ai(actors[2], 'alphabot');
|
|
// type, affiliation, lifespan, xpos, ypos, xvel, yvel
|
|
particles[0] = new Particle('mouse', 0, 10000000000, 0, 0, 0, 0);
|
|
|
|
camera = [actors[0]]; // Set camera.
|
|
canvas.style.background = '#fff'; // Set canvas style.
|
|
level = 0 // Set level
|
|
canvas.style.display = 'block'; // Set up canvas
|
|
canvas.style.border = '1px solid #ddd';
|
|
spritesheet = new Image(); // Define spritesheet
|
|
spritesheet.src = 'actors.png';
|
|
document.addEventListener('keydown', keyDown, true); // Add key events
|
|
document.addEventListener('keyup', keyUp, true);
|
|
canvas.addEventListener('mousemove', function(evt){mouse = getMouse(evt)}, false);
|
|
animate();
|
|
}
|
|
|
|
// Define the level.
|
|
levels = [
|
|
['#...................#']
|
|
,['#...................#']
|
|
,['########............#']
|
|
,['################...##']
|
|
,['#####################']
|
|
]
|
|
|
|
function animate() {
|
|
requestAnimFrame(animate);
|
|
loopGame();
|
|
}
|
|
|
|
// Round a number.
|
|
function r(num){
|
|
return Math.round(num);
|
|
}
|
|
|
|
// Add key pressed to key list.
|
|
function keyDown(e){
|
|
var keyPress;
|
|
if (typeof event !== 'undefined') {
|
|
keyPress = event.keyCode;
|
|
}else if(e){
|
|
keyPress = e.which;
|
|
}
|
|
if(keys.indexOf(keyPress) == -1){
|
|
keys.push(keyPress);
|
|
}
|
|
}
|
|
|
|
// Remove key from key list.
|
|
function keyUp(e){
|
|
var keyPress;
|
|
if (typeof event !== 'undefined') {
|
|
keyPress = event.keyCode;
|
|
}else if(e){
|
|
keyPress = e.which;
|
|
}
|
|
keys.splice(keys.indexOf(keyPress), 1);
|
|
}
|
|
|
|
// Define objects.
|
|
|
|
function Controller(object, actions){
|
|
this.actor = object;
|
|
this.checkKeys = function(){
|
|
for(i in actions){
|
|
if(keys.indexOf(actions[i][0]) > -1){
|
|
this.actor.action(actions[i][1]);
|
|
}
|
|
}
|
|
this.actor.refreshActions();
|
|
}
|
|
}
|
|
|
|
function Ai(object, ai){
|
|
this.actor = object;
|
|
this.aivars = [0, 0, 0];
|
|
this.run = function(){
|
|
switch(ai){
|
|
case 'alphaBot': // Work in progress following melee AI
|
|
var playerIndex = -1;
|
|
var topDistance = 400;
|
|
for(i in actors){
|
|
if(actors[i].image == 0 && Math.abs(actors[i].x - this.actor) < topDistance){
|
|
playerIndex = i;
|
|
topDistance = Math.abs(actors[i].x - this.actor) - 50;
|
|
}
|
|
}
|
|
if(playerIndex >= 0){
|
|
|
|
}
|
|
break;
|
|
case 'pace': // Test AI
|
|
if(this.aivars[0] == 0 || this.actor.x < -300 || this.actor.x > 300){
|
|
this.aivars[0] = (this.actor.x > 0 ? -1 : 1);
|
|
}
|
|
if(this.xvel == 0){
|
|
this.action('jump');
|
|
}
|
|
this.actor.xvel += ((0.02 * this.aivars[0]) * speed);
|
|
break;
|
|
case 'still':
|
|
this.actor.x = mouse.x + lookx;
|
|
this.actor.y = mouse.y + looky;
|
|
this.actor.xvel = 0;
|
|
this.actor.yvel = 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Actor class for all solid cubes
|
|
function Actor(image, type, health, power, xpos, ypos){
|
|
this.image = image;
|
|
this.group = type;
|
|
this.health = health;
|
|
this.power = power;
|
|
this.xvel = this.yvel = this.jump = 0;
|
|
this.imageLoad = 2;
|
|
this.right = this.left = this.up = this.down = false;
|
|
this.box = new Box(this.x, this.y, 16, 16, this.xvel, this.yvel, ['player', 'pacer']); // Set physics class for this actor
|
|
this.x = xpos;
|
|
this.y = ypos;
|
|
this.oneactions = [];
|
|
this.actionsturn = [];
|
|
this.index = actors.length;
|
|
this.vars = [false, false, false]; // for use by AIs to control particles
|
|
|
|
this.refreshActions = function(){
|
|
this.oneactions = [];
|
|
for(i in this.actionsturn){
|
|
this.oneactions.push(this.actionsturn[i]);
|
|
}
|
|
}
|
|
|
|
// Actions to call from controllers
|
|
this.action = function(type){
|
|
switch(type){
|
|
case 'moveLeft':
|
|
this.xvel -= (0.08 * speed);
|
|
break;
|
|
case 'moveRight':
|
|
this.xvel += (0.08 * speed);
|
|
break;
|
|
case 'jump':
|
|
this.yvel = (this.down ? -4 - this.power : this.yvel);
|
|
break;
|
|
case 'melee':
|
|
this.yvel = 10;
|
|
break;
|
|
case 'camera':
|
|
camera = [this];
|
|
break;
|
|
case 'stream':
|
|
particles.push(new Particle(0, 0, 60000, this.x, this.y - 1, this.xvel + ((this.x - (lookx + 250)) + (mouse.x - 250)) / 30, -3 + this.yvel * 3));
|
|
sound.shoot1.play();
|
|
break;
|
|
case 'bounce':
|
|
particles.push(new Particle(1, 0, 5000, this.x, this.y - 1, this.xvel * 2 + ((Math.random() - 0.5) * 5), -5 + this.yvel * 3));
|
|
break;
|
|
case 'flo':
|
|
particles.push(new Particle(2, 0, 100000, this.x, this.y - 16, this.xvel * 4 + ((Math.random() - 0.5) * 10), -10));
|
|
break;
|
|
case 'dark':
|
|
particles.push(new Particle(3, this.index, 3000, this.x, this.y, this.xvel, this.yvel));
|
|
this.vars = [false, false, false]
|
|
break;
|
|
case 'shoot':
|
|
this.vars = [true, this.xvel + (mouse.x + 250 - (this.x - lookx + 250)) / 30, this.yvel + (mouse.y - this.y) / 30];
|
|
break;
|
|
}
|
|
this.actionsturn.push(type);
|
|
}
|
|
|
|
this.xCheck = function(){
|
|
this.left = this.right = false;
|
|
for(j in actors){
|
|
if(actors[j].image != this.image){
|
|
if(actors[j].x + 16 > this.x && this.x > actors[j].x && this.sameY(actors[j])){
|
|
this.left = true;
|
|
this.x = actors[j].x + 16;
|
|
this.xvel = (this.xvel < 0 ? 0 : this.xvel);
|
|
}
|
|
if(this.x + 16 > actors[j].x && this.x < actors[j].x && this.sameY(actors[j])){
|
|
this.right = true;
|
|
this.x = actors[j].x - 16;
|
|
this.xvel = (this.xvel > 0 ? 0 : this.xvel);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.yCheck = function(){
|
|
this.up = this.down = false;
|
|
|
|
if(this.y >= 216){
|
|
this.down = true;
|
|
this.y = 216;
|
|
}
|
|
|
|
for(j in actors){
|
|
if(actors[j].image != this.image){
|
|
if(this.y + 16 > actors[j].y && this.y < actors[j].y + 16 && this.sameX(actors[j])){
|
|
if(this.y < actors[j].y){
|
|
this.down = true;
|
|
this.y = actors[j].y - 16;
|
|
}else{
|
|
this.up = true;
|
|
this.y = actors[j].y + 16;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.sameY = function(obj){
|
|
return (this.y < obj.y + 15 && this.y + 16 > obj.y);
|
|
}
|
|
|
|
this.sameX = function(obj){
|
|
return (this.x + 15 > obj.x && this.x < obj.x + 15);
|
|
}
|
|
|
|
this.simulate = function(){
|
|
this.y += this.yvel;
|
|
this.yCheck();
|
|
if(this.down || this.up){
|
|
if(Math.abs(this.yvel) > 8){
|
|
this.yvel *= -0.2;
|
|
this.y -= 0;
|
|
}else{
|
|
this.yvel = 0;
|
|
}
|
|
}else{
|
|
this.yvel += 0.03 * speed;
|
|
}
|
|
this.x += this.xvel;
|
|
this.xCheck();
|
|
this.xvel *= Math.pow(0.992, speed);
|
|
}
|
|
|
|
this.draw = function(){
|
|
var reflect = 100; // Depth reflection goes before fading completely
|
|
var drawx = r(this.x - lookx);
|
|
var drawy =
|
|
context.drawImage(spritesheet, this.image * 16, 16, 16, 16, drawx, r(this.y - 16 - looky), 16, 16);
|
|
context.globalAlpha = 1;
|
|
context.drawImage(spritesheet, this.image * 16, 16, 16, 16, drawx, r((216 - (this.y - 216)) - looky), 16, 16);
|
|
// StartX, StartY, EndX, EndY
|
|
var gradient = context.createLinearGradient(drawx, r((216 - this.y + 216) - looky - 5), drawx, r((214 - (this.y - 216)) - looky) + 16);
|
|
gradient.addColorStop(0.1, 'rgba(255, 255, 255, ' + (this.y < 120 ? 1 : ((200 - this.y) / 35) + 0.2) +')');
|
|
gradient.addColorStop(0.9, 'rgba(255, 255, 255, 1)');
|
|
context.fillStyle = gradient;
|
|
context.fillRect(drawx, r((216 - (this.y - 216)) - looky), 16, 16);
|
|
context.globalAlpha = 1;
|
|
context.fillStyle = "#444";
|
|
context.font = "10pt Arial";
|
|
context.textAlign = 'left';
|
|
context.fillText('Health: ' + actors[0].health, 10, 290);
|
|
context.fillText('X: ' + r(actors[0].x), 10, 310);
|
|
context.fillText('Y: ' + r(actors[0].y), 70, 310);
|
|
context.textAlign = 'right';
|
|
context.fillText('Sint version 0.2.1', 490, 310);
|
|
}
|
|
//this.xvel += (canMove(xpos, ypos, '-1', this.xvel));
|
|
}
|
|
|
|
function Particle(type, affiliation, lifespan, xpos, ypos, xvel, yvel){
|
|
this.x = xpos;
|
|
this.y = ypos;
|
|
this.xvel = xvel;
|
|
this.yvel = yvel;
|
|
this.type = type;
|
|
this.life = lifespan;
|
|
this.size = [3, 5, 7, 5][type];
|
|
this.created = this.timeup = new Date();
|
|
this.timeup = new Date(this.timeup.getTime() + lifespan);
|
|
this.deleteme = false;
|
|
this.aff = affiliation;
|
|
this.vars = [false, false];
|
|
var angle = Math.random() * 360;
|
|
this.addx = Math.sin(angle) * ((particles.length + 200) / 5);
|
|
this.addy = Math.cos(angle) * ((particles.length + 200) / 10);
|
|
|
|
this.draw = function(){
|
|
//context.beginPath();
|
|
//context.rect(this.x, this.y, 3, 3);
|
|
switch(this.type){
|
|
case 'mouse':
|
|
context.globalAlpha = 0.7;
|
|
context.lineWidth = 2;
|
|
context.strokeStyle = '#33d';
|
|
context.strokeRect(mouse.x - this.vars[0] / 2, mouse.y - this.vars[0] / 2, this.vars[0], this.vars[0]);
|
|
context.globalAlpha = 1;
|
|
break;
|
|
case 0:
|
|
context.globalAlpha = 1;
|
|
context.lineWidth = 1;
|
|
context.strokeStyle = '#66b';
|
|
context.strokeRect(r(this.x - lookx) + 0.5, r(this.y - looky) + 0.5, 2, 2);
|
|
context.strokeStyle = '#aaf';
|
|
context.strokeRect(r(this.x - lookx) + 0.5, r(213 - (this.y - 216) - looky) + 0.5, 2, 2);
|
|
break;
|
|
case 1:
|
|
context.globalAlpha = 1;
|
|
context.lineWidth = 2;
|
|
context.strokeStyle = '#b79';
|
|
context.strokeRect(r(this.x - lookx) + 0.5, r(this.y - looky) + 0.5, 4, 4);
|
|
context.strokeStyle = '#fbd';
|
|
context.strokeRect(r(this.x - lookx) + 0.5, r(213 - (this.y - 216) - looky) + 0.5, 4, 4);
|
|
break;
|
|
case 2:
|
|
context.globalAlpha = 0.2;
|
|
context.lineWidth = 1;
|
|
context.strokeStyle = '#363';
|
|
context.strokeRect(r(this.x - lookx) + 0.5, r(this.y - looky) + 0.5, 5, 5);
|
|
context.strokeStyle = '#9c9';
|
|
context.strokeRect(r(this.x - lookx) + 0.5, r(213 - (this.y - 216) - looky) + 0.5, 5, 5);
|
|
context.globalAlpha = 1;
|
|
break;
|
|
case 3:
|
|
context.globalAlpha = 0.5;
|
|
context.lineWidth = 1;
|
|
context.strokeStyle = '#000';
|
|
context.strokeRect(r(this.x - lookx) + 0.5, r(this.y - looky) + 0.5, 4, 4);
|
|
context.strokeStyle = '#aaa';
|
|
context.strokeRect(r(this.x - lookx) + 0.5, r(213 - (this.y - 216) - looky) + 0.5, 4, 4);
|
|
context.globalAlpha = 1;
|
|
}
|
|
}
|
|
|
|
this.onGround = function(){
|
|
return (this.y > 216 - this.size);
|
|
}
|
|
|
|
this.simulate = function(){
|
|
switch(this.type){
|
|
case 'mouse':
|
|
if(this.vars[0] == false){
|
|
this.vars[0] = 8;
|
|
}
|
|
if(this.vars[1] == true){
|
|
this.vars[0] += 0.4;
|
|
if(this.vars[0] >= 10){
|
|
this.vars[1] = false;
|
|
}
|
|
}else{
|
|
this.vars[0] -= 0.4;
|
|
if(this.vars[0] <= 7){
|
|
this.vars[1] = true;
|
|
}
|
|
}
|
|
break;
|
|
case 0:
|
|
this.yvel += (this.onGround() ? 0 : 0.01 * speed);
|
|
this.xvel *= Math.pow(0.998, speed);;
|
|
this.x += this.xvel;
|
|
this.y += this.yvel;
|
|
if(this.onGround()){
|
|
this.y = 216 - this.size;
|
|
this.yvel = 0;
|
|
}
|
|
break;
|
|
case 1:
|
|
this.yvel += (this.onGround() ? 0 : 0.007 * speed);
|
|
this.xvel *= Math.pow(0.999, speed);
|
|
this.x += this.xvel;
|
|
this.y += this.yvel;
|
|
if(this.onGround()){
|
|
this.y = 216 - this.size;
|
|
this.yvel = (this.yvel > 2 ? this.yvel * -0.7 : 0);
|
|
}
|
|
break;
|
|
case 2:
|
|
this.yvel *= Math.pow(0.996, speed);
|
|
this.xvel *= Math.pow(0.996, speed);
|
|
this.x += this.xvel;
|
|
this.y += this.yvel;
|
|
if(this.onGround()){
|
|
this.y = 216 - this.size;
|
|
this.yvel = 0;
|
|
}
|
|
for(j in camera){
|
|
var distance = Math.sqrt(Math.pow(camera[j].x - this.x, 2) + Math.pow(camera[j].y - this.y, 2));
|
|
var xmov = camera[j].x - ((this.addx / (distance / 10)) + 8) - this.x;
|
|
var ymov = camera[j].y - ((this.addy / (distance / 10)) + 0) - this.y;
|
|
this.xvel += (xmov > 0 ? 1 : -1) / (((distance + 10) / 5) / Math.abs(xmov / 10)) + (Math.random() - 0.5) * 0.4;
|
|
this.yvel += (ymov > 0 ? 1 : -1) / (((distance + 10) / 5) / Math.abs(ymov / 10)) + (Math.random() - 0.5) * 0.4;
|
|
if(distance < 35 && this.y < camera[j].y && camera[j].yvel > -1){
|
|
camera[j].yvel -= (this.y < 50 ? this.y / 3000 : 0.05)
|
|
}
|
|
}
|
|
break;
|
|
case 3:
|
|
this.yvel *= Math.pow(0.996, speed);
|
|
this.xvel *= Math.pow(0.996, speed);
|
|
this.x += this.xvel;
|
|
this.y += this.yvel;
|
|
var parent = actors[this.aff];
|
|
if(typeof this.orbit === 'undefined'){
|
|
var angle = Math.random() * 360;
|
|
this.orbit = {
|
|
cube: this.affiliation,
|
|
x: 9 * Math.cos(angle * Math.PI / 180),
|
|
y: 9 * Math.sin(angle * Math.PI / 180)
|
|
}
|
|
}
|
|
if(parent.vars[0] == false && this.vars[0] == false){
|
|
this.xvel += (this.orbit.x + parent.x + 6 - this.x) / 10;
|
|
this.yvel += (this.orbit.y + parent.y - 20 - this.y) / 10;
|
|
}else{
|
|
if(this.vars[1] == false){
|
|
this.xvel = parent.vars[1] + (Math.random() - 0.5) * 3;
|
|
this.yvel = parent.vars[2] + (Math.random() - 0.5) * 3;
|
|
this.vars[1] = true;
|
|
}
|
|
this.vars[0] = true;
|
|
}
|
|
if(this.onGround()){
|
|
this.y = 216 - this.size;
|
|
this.yvel = 0;
|
|
}
|
|
break;
|
|
}
|
|
if(thisLoop > this.timeup){
|
|
this.deleteme = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Collision detection class
|
|
function Box(x, y, w, h, xvel, yvel, colgroup){
|
|
this.x = x;
|
|
this.y = y;
|
|
this.width = w;
|
|
this.height = h;
|
|
this.col = colgroup;
|
|
this.left = this.right = this.up = this.down = false;
|
|
|
|
this.reset = function(){
|
|
this.down = this.up = this.left = this.right = false;
|
|
}
|
|
|
|
this.collide = function(){
|
|
// Check for collision with level
|
|
var lv = levels[level];
|
|
var colareax = ((this.width - 2) >> 4) + 2;
|
|
var colareay = ((this.height - 2) >> 4) + 2;
|
|
var any = false;
|
|
for(var hr = 0; hr < colareax; hr++){
|
|
for(var vr = 0; vr < colareay; ve++){
|
|
if(lv[(this.x + (16 * hr))][(this.y + (16 * vr))] == '#'){
|
|
any = true;
|
|
}
|
|
}
|
|
}
|
|
// Check for collision with other boxes in same collision group
|
|
}
|
|
|
|
this.move = function(){
|
|
this.x += xvel;
|
|
this.y += yvel;
|
|
this.reset();
|
|
|
|
}
|
|
}
|
|
|
|
// Run game.
|
|
|
|
var speed;
|
|
var lastLoop = new Date();
|
|
var thisLoop = lastLoop;
|
|
function loopGame(){
|
|
canvas.style.margin = (window.innerHeight > 360 ? window.innerHeight / 2 - 180 + 'px auto' : '10px auto');
|
|
lastLoop = thisLoop;
|
|
thisLoop = new Date();
|
|
speed = (thisLoop - lastLoop);
|
|
speed = (speed > 50 ? 50 : speed);
|
|
context.clearRect(0, 0, 500, 350);
|
|
lookx = looky = 0;
|
|
looky = -0;
|
|
for(i in camera){
|
|
lookx += (camera[i] instanceof Array ? camera[i][0] : camera[i].x) - 250;
|
|
// looky += (camera[i] == instanceof Array ? camera[i][1] : camera[i].y) - 175;
|
|
}
|
|
lookx /= camera.length;
|
|
looky /= camera.length;
|
|
/*
|
|
if( instanceof Array){
|
|
lookx = camera[0];
|
|
looky = camera[1];
|
|
}else{
|
|
lookx = camera.x - 250 + camera.xvel * 5;
|
|
looky = 0;
|
|
}
|
|
*/
|
|
for(i in controllers){
|
|
controllers[i].checkKeys();
|
|
}
|
|
for(i in actors){
|
|
actors[i].simulate();
|
|
actors[i].draw();
|
|
}
|
|
for(i in ais){
|
|
ais[i].run();
|
|
}
|
|
for(i in particles){
|
|
particles[i].simulate()
|
|
particles[i]. draw()
|
|
if(particles[i].deleteme || particles.length > 3000){
|
|
particles.splice(i, 1);
|
|
i--;
|
|
}
|
|
}
|
|
// Slow down game to test low framerates
|
|
/*
|
|
for(var j=1; j < 10000000; j++){
|
|
j = j;
|
|
}
|
|
*/
|
|
} |