1449 lines
56 KiB
JavaScript
1449 lines
56 KiB
JavaScript
//global player variables
|
|
let player, jumpSensor, playerBody, playerHead, headSensor;
|
|
|
|
// player Object Prototype *********************************************
|
|
mech = {
|
|
|
|
}
|
|
|
|
const mech = {
|
|
spawn: function () {
|
|
//player as a series of vertices
|
|
let vector = Vertices.fromPath('0 40 0 115 20 130 30 130 50 115 50 40');
|
|
playerBody = Matter.Bodies.fromVertices(0, 0, vector);
|
|
//this sensor check if the player is on the ground to enable jumping
|
|
jumpSensor = Bodies.rectangle(0, 46, 36, 6, {
|
|
sleepThreshold: 99999999999,
|
|
isSensor: true,
|
|
});
|
|
//this part of the player lowers on crouch
|
|
vector = Vertices.fromPath('0 -66 18 -82 0 -37 50 -37 50 -66 32 -82');
|
|
playerHead = Matter.Bodies.fromVertices(0, -55, vector);
|
|
//a part of player that senses if the player's head is empty and can return after crouching
|
|
headSensor = Bodies.rectangle(0, -57, 48, 45, {
|
|
sleepThreshold: 99999999999,
|
|
isSensor: true,
|
|
});
|
|
player = Body.create({ //combine jumpSensor and playerBody
|
|
parts: [playerBody, playerHead, jumpSensor, headSensor],
|
|
inertia: Infinity, //prevents player rotation
|
|
friction: 0.002,
|
|
//frictionStatic: 0.5,
|
|
restitution: 0.3,
|
|
sleepThreshold: Infinity,
|
|
collisionFilter: {
|
|
group: 0,
|
|
category: 0x001000,
|
|
mask: 0x010001
|
|
},
|
|
});
|
|
//Matter.Body.setPosition(player, mech.spawnPos);
|
|
//Matter.Body.setVelocity(player, mech.spawnVel);
|
|
Matter.Body.setMass(player, mech.mass);
|
|
World.add(engine.world, [player]);
|
|
//holding body constraint
|
|
const holdConstraint = Constraint.create({
|
|
pointA: {
|
|
x: 0,
|
|
y: 0
|
|
},
|
|
//setting constaint to jump sensor because it has to be on something until the player picks up things
|
|
bodyB: jumpSensor,
|
|
stiffness: 0.4,
|
|
});
|
|
World.add(engine.world, holdConstraint);
|
|
},
|
|
width: 50,
|
|
radius: 30,
|
|
fillColor: '#fff',
|
|
fillColorDark: '#ddd',
|
|
fireCDcycle: 0,
|
|
gun: 'machine', //current gun in use
|
|
gunOptions: { //keeps track of keys that switch guns (used in the onkeypress event)
|
|
49: 'machine',
|
|
50: 'needle',
|
|
51: 'shot',
|
|
52: 'rail',
|
|
53: 'cannon',
|
|
54: 'super',
|
|
55: 'lob',
|
|
// 55: 'spiritBomb',
|
|
// 56: 'experimental'
|
|
}
|
|
height: 42,
|
|
yOffWhen: {
|
|
crouch: 22,
|
|
stand: 49,
|
|
jump: 70
|
|
},
|
|
yOff: 70,
|
|
yOffGoal: 70,
|
|
onGround: false, //checks if on ground or in air
|
|
onBody: {
|
|
id: 0,
|
|
index: 0,
|
|
type: "map",
|
|
action: ''
|
|
},
|
|
numTouching = 0,
|
|
crouch = false,
|
|
isHeadClear = true,
|
|
spawnPos = {
|
|
x: 0,
|
|
y: 0
|
|
},
|
|
spawnVel = {
|
|
x: 0,
|
|
y: 0
|
|
},
|
|
pos = {
|
|
x: this.spawnPos.x,
|
|
y: this.spawnPos.y
|
|
},
|
|
setPosToSpawn = function (xPos, yPos) {
|
|
this.spawnPos.x = xPos
|
|
this.spawnPos.y = yPos
|
|
this.pos.x = xPos;
|
|
this.pos.y = yPos;
|
|
this.Vx = this.spawnVel.x;
|
|
this.Vy = this.spawnVel.y;
|
|
Matter.Body.setPosition(player, this.spawnPos);
|
|
Matter.Body.setVelocity(player, this.spawnVel);
|
|
};
|
|
this.Sy = this.pos.y; //adds a smoothing effect to vertical only
|
|
this.Vx = 0;
|
|
this.Vy = 0;
|
|
this.VxMax = 7;
|
|
this.mass = 5;
|
|
this.Fx = 0.004 * this.mass; //run Force on ground
|
|
this.FxAir = 0.0006 * this.mass; //run Force in Air
|
|
this.Fy = -0.05 * this.mass; //jump Force
|
|
this.angle = 0;
|
|
this.walk_cycle = 0;
|
|
this.stepSize = 0;
|
|
this.flipLegs = -1;
|
|
this.hip = {
|
|
x: 12,
|
|
y: 24,
|
|
};
|
|
this.knee = {
|
|
x: 0,
|
|
y: 0,
|
|
x2: 0,
|
|
y2: 0
|
|
};
|
|
this.foot = {
|
|
x: 0,
|
|
y: 0
|
|
};
|
|
this.legLength1 = 55;
|
|
this.legLength2 = 45;
|
|
this.canvasX = canvas.width / 2;
|
|
this.canvasY = canvas.height / 2;
|
|
this.transX = this.canvasX - this.pos.x;
|
|
this.transY = this.canvasX - this.pos.x;
|
|
this.mouse = {
|
|
x: canvas.width / 3,
|
|
y: canvas.height
|
|
};
|
|
this.getMousePos = function (x, y) {
|
|
this.mouse.x = x;
|
|
this.mouse.y = y;
|
|
};
|
|
this.testingMoveLook = function () {
|
|
//move
|
|
this.pos.x = player.position.x;
|
|
this.pos.y = playerBody.position.y - this.yOff;
|
|
this.Vx = player.velocity.x;
|
|
this.Vy = player.velocity.y;
|
|
//look
|
|
this.canvasX = canvas.width / 2
|
|
this.canvasY = canvas.height / 2
|
|
this.transX = this.canvasX - this.pos.x;
|
|
this.transY = this.canvasY - this.pos.y;
|
|
this.angle = Math.atan2(this.mouse.y - this.canvasY, this.mouse.x - this.canvasX);
|
|
}
|
|
|
|
this.move = function () {
|
|
this.pos.x = player.position.x;
|
|
this.pos.y = playerBody.position.y - this.yOff;
|
|
this.Vx = player.velocity.x;
|
|
this.Vy = player.velocity.y;
|
|
};
|
|
this.look = function () {
|
|
//set a max on mouse look
|
|
let mX = this.mouse.x;
|
|
if (mX > canvas.width * 0.8) {
|
|
mX = canvas.width * 0.8;
|
|
} else if (mX < canvas.width * 0.2) {
|
|
mX = canvas.width * 0.2;
|
|
}
|
|
let mY = this.mouse.y;
|
|
if (mY > canvas.height * 0.8) {
|
|
mY = canvas.height * 0.8;
|
|
} else if (mY < canvas.height * 0.2) {
|
|
mY = canvas.height * 0.2;
|
|
}
|
|
//set mouse look
|
|
this.canvasX = this.canvasX * 0.94 + (canvas.width - mX) * 0.06;
|
|
this.canvasY = this.canvasY * 0.94 + (canvas.height - mY) * 0.06;
|
|
|
|
//set translate values
|
|
this.transX = this.canvasX - this.pos.x;
|
|
this.Sy = 0.99 * this.Sy + 0.01 * (this.pos.y);
|
|
//hard caps how behind y position tracking can get.
|
|
if (this.Sy - this.pos.y > canvas.height / 2) {
|
|
this.Sy = this.pos.y + canvas.height / 2;
|
|
} else if (this.Sy - this.pos.y < -canvas.height / 2) {
|
|
this.Sy = this.pos.y - canvas.height / 2;
|
|
}
|
|
this.transY = this.canvasY - this.Sy; //gradual y camera tracking
|
|
//this.transY = this.canvasY - this.pos.y; //normal y camera tracking
|
|
|
|
//make player head angled towards mouse
|
|
//this.angle = Math.atan2(this.mouse.y - this.canvasY, this.mouse.x - this.canvasX);
|
|
this.angle = Math.atan2(this.mouse.y + (this.Sy - this.pos.y) * game.zoom - this.canvasY, this.mouse.x - this.canvasX);
|
|
};
|
|
this.doCrouch = function () {
|
|
if (!this.crouch) {
|
|
this.crouch = true;
|
|
this.yOffGoal = this.yOffWhen.crouch;
|
|
Matter.Body.translate(playerHead, {
|
|
x: 0,
|
|
y: 40
|
|
})
|
|
}
|
|
}
|
|
this.undoCrouch = function () {
|
|
this.crouch = false;
|
|
this.yOffGoal = this.yOffWhen.stand;
|
|
Matter.Body.translate(playerHead, {
|
|
x: 0,
|
|
y: -40
|
|
})
|
|
}
|
|
this.enterAir = function () {
|
|
this.onGround = false;
|
|
player.frictionAir = 0.001;
|
|
if (this.isHeadClear) {
|
|
if (this.crouch) {
|
|
this.undoCrouch();
|
|
}
|
|
this.yOffGoal = this.yOffWhen.jump;
|
|
};
|
|
}
|
|
this.enterLand = function () {
|
|
this.onGround = true;
|
|
if (this.crouch) {
|
|
if (this.isHeadClear) {
|
|
this.undoCrouch();
|
|
player.frictionAir = 0.12;
|
|
} else {
|
|
this.yOffGoal = this.yOffWhen.crouch;
|
|
player.frictionAir = 0.5;
|
|
}
|
|
} else {
|
|
this.yOffGoal = this.yOffWhen.stand;
|
|
player.frictionAir = 0.12;
|
|
}
|
|
};
|
|
this.buttonCD_jump = 0; //cooldown for player buttons
|
|
this.keyMove = function () {
|
|
if (this.onGround) { //on ground **********************
|
|
if (this.crouch) { //crouch
|
|
if (!(keys[40] || keys[83]) && this.isHeadClear) { //not pressing crouch anymore
|
|
this.undoCrouch();
|
|
player.frictionAir = 0.12;
|
|
}
|
|
} else if (keys[40] || keys[83]) { //on ground && not crouched and pressing s or down
|
|
this.doCrouch();
|
|
player.frictionAir = 0.5;
|
|
} else if ((keys[32] || keys[38] || keys[87]) && this.buttonCD_jump + 20 < game.cycle) { //jump
|
|
this.buttonCD_jump = game.cycle; //can't jump until 20 cycles pass
|
|
Matter.Body.setVelocity(player, { //zero player velocity for consistant jumps
|
|
x: player.velocity.x,
|
|
y: 0
|
|
});
|
|
player.force.y = this.Fy / game.delta; //jump force / delta so that force is the same on game slowdowns
|
|
}
|
|
//horizontal move on ground
|
|
if (keys[37] || keys[65]) { //left or a
|
|
if (player.velocity.x > -this.VxMax) {
|
|
player.force.x += -this.Fx / game.delta;
|
|
}
|
|
} else if (keys[39] || keys[68]) { //right or d
|
|
if (player.velocity.x < this.VxMax) {
|
|
player.force.x += this.Fx / game.delta;
|
|
}
|
|
}
|
|
|
|
} else { // in air **********************************
|
|
//check for short jumps
|
|
if (this.buttonCD_jump + 60 > game.cycle && //just pressed jump
|
|
!(keys[32] || keys[38] || keys[87]) && //but not pressing jump key
|
|
this.Vy < 0) { // and velocity is up
|
|
Matter.Body.setVelocity(player, { //reduce player velocity every cycle until not true
|
|
x: player.velocity.x,
|
|
y: player.velocity.y * 0.94
|
|
});
|
|
}
|
|
if (keys[37] || keys[65]) { // move player left / a
|
|
if (player.velocity.x > -this.VxMax + 2) {
|
|
player.force.x += -this.FxAir / game.delta;
|
|
}
|
|
} else if (keys[39] || keys[68]) { //move player right / d
|
|
if (player.velocity.x < this.VxMax - 2) {
|
|
player.force.x += this.FxAir / game.delta;
|
|
}
|
|
}
|
|
}
|
|
//smoothly move height towards height goal ************
|
|
this.yOff = this.yOff * 0.85 + this.yOffGoal * 0.15
|
|
};
|
|
this.death = function () {
|
|
location.reload();
|
|
//Matter.Body.setPosition(player, this.spawnPos);
|
|
//Matter.Body.setVelocity(player, this.spawnVel);
|
|
//this.dropBody();
|
|
//game.zoom = 0; //zooms out all the way
|
|
//this.health = 1;
|
|
}
|
|
this.health = 1;
|
|
this.regen = function () {
|
|
if (this.health < 1 && game.cycle % 15 === 0) {
|
|
this.addHealth(0.01);
|
|
}
|
|
};
|
|
this.drawHealth = function () {
|
|
if (this.health < 1) {
|
|
ctx.fillStyle = 'rgba(100, 100, 100, 0.5)';
|
|
ctx.fillRect(this.pos.x - this.radius, this.pos.y - 50, 60, 10);
|
|
ctx.fillStyle = "#f00";
|
|
ctx.fillRect(this.pos.x - this.radius, this.pos.y - 50, 60 * this.health, 10);
|
|
}
|
|
};
|
|
this.addHealth = function (heal) {
|
|
this.health += heal;
|
|
if (this.health > 1) this.health = 1;
|
|
}
|
|
this.damage = function (dmg) {
|
|
this.health -= dmg;
|
|
if (this.health <= 0) {
|
|
this.death();
|
|
}
|
|
}
|
|
this.deathCheck = function () {
|
|
if (this.pos.y > game.fallHeight) { // if player is 4000px deep reset to spawn Position and Velocity
|
|
this.death();
|
|
}
|
|
};
|
|
this.hitMob = function (i, dmg) {
|
|
this.damage(dmg);
|
|
playSound("dmg" + Math.floor(Math.random() * 4));
|
|
//extra kick between player and mob
|
|
//this section would be better with forces but they don't work...
|
|
let angle = Math.atan2(player.position.y - mob[i].position.y, player.position.x - mob[i].position.x);
|
|
Matter.Body.setVelocity(player, {
|
|
x: player.velocity.x + 8 * Math.cos(angle),
|
|
y: player.velocity.y + 8 * Math.sin(angle)
|
|
});
|
|
Matter.Body.setVelocity(mob[i], {
|
|
x: mob[i].velocity.x - 8 * Math.cos(angle),
|
|
y: mob[i].velocity.y - 8 * Math.sin(angle)
|
|
});
|
|
}
|
|
|
|
this.buttonCD = 0; //cooldown for player buttons
|
|
this.eaten = [];
|
|
this.eat = function () {
|
|
if (keys[81] && this.buttonCD < game.cycle) {
|
|
this.buttonCD = game.cycle + 5;
|
|
this.findClosestBody();
|
|
let i = this.closest.index
|
|
if (this.closest.dist < 150) {
|
|
//draw eating
|
|
ctx.lineWidth = 10;
|
|
ctx.strokeStyle = '#5f9';
|
|
ctx.beginPath();
|
|
ctx.moveTo(this.pos.x + 15 * Math.cos(this.angle), this.pos.y + 15 * Math.sin(this.angle));
|
|
ctx.lineTo(body[i].position.x, body[i].position.y);
|
|
ctx.stroke();
|
|
//set to eaten
|
|
body[i].eaten = true;
|
|
//drop body
|
|
body[i].frictionAir = 0;
|
|
this.isHolding = false;
|
|
holdConstraint.bodyB = jumpSensor; //set on sensor to get the constraint on somethign else
|
|
//add to eaten
|
|
body[i].collisionFilter.category = 0x000000;
|
|
body[i].collisionFilter.mask = 0x000000;
|
|
//Matter.Body.setStatic(body[i], true)
|
|
Matter.Sleeping.set(body[i], true)
|
|
Matter.Body.scale(body[i], 0.5, 0.5)
|
|
Matter.Body.setVelocity(body[i], {
|
|
x: 0,
|
|
y: 0
|
|
});
|
|
Matter.Body.setAngularVelocity(body[i], 0.08) //(Math.random()*0.5-1)*0.1)
|
|
//Matter.World.remove(engine.world, body[i]);
|
|
//body.splice(i, 1);
|
|
this.eaten[this.eaten.length] = {
|
|
index: i,
|
|
cycle: game.cycle
|
|
}
|
|
}
|
|
}
|
|
//control behavior of eaten bodies
|
|
for (let j = 0; j < this.eaten.length; j++) {
|
|
const pos = {
|
|
x: this.pos.x + 60 * Math.cos((game.cycle + this.eaten[j].cycle) * 0.05),
|
|
y: this.pos.y + 30 * Math.sin((game.cycle + this.eaten[j].cycle) * 0.05),
|
|
}
|
|
Matter.Body.setPosition(body[this.eaten[j].index], pos);
|
|
//Matter.Body.setVelocity(body[this.eaten[j].index],{x:0, y:0});
|
|
|
|
}
|
|
|
|
if (keys[69] && this.buttonCD < game.cycle && this.eaten.length) {
|
|
this.buttonCD = game.cycle + 5;
|
|
let i = this.eaten[this.eaten.length - 1].index;
|
|
body[i].eaten = false;
|
|
body[i].collisionFilter.category = 0x000001;
|
|
body[i].collisionFilter.mask = 0x000101;
|
|
Matter.Body.setVelocity(body[i], {
|
|
x: 0,
|
|
y: 0
|
|
});
|
|
//Matter.Body.setStatic(body[i], false)
|
|
Matter.Sleeping.set(body[i], false)
|
|
Matter.Body.scale(body[i], 2, 2);
|
|
Matter.Body.setPosition(body[i], {
|
|
x: this.pos.x + 20 * Math.cos(this.angle),
|
|
y: this.pos.y + 20 * Math.sin(this.angle)
|
|
});
|
|
const impulse = 0.06 * body[i].mass;
|
|
const f = {
|
|
x: impulse * Math.cos(this.angle) / game.delta,
|
|
y: impulse * Math.sin(this.angle) / game.delta
|
|
}
|
|
body[i].force = f;
|
|
this.eaten.pop();
|
|
}
|
|
};
|
|
|
|
this.holdKeyDown = 0;
|
|
this.keyHold = function () { //checks for holding/dropping/picking up bodies
|
|
if (this.isHolding) {
|
|
//give the constaint more length and less stiffness if it is pulled out of position
|
|
const Dx = body[this.holdingBody].position.x - holdConstraint.pointA.x;
|
|
const Dy = body[this.holdingBody].position.y - holdConstraint.pointA.y;
|
|
holdConstraint.length = Math.sqrt(Dx * Dx + Dy * Dy) * 0.95;
|
|
holdConstraint.stiffness = -0.01 * holdConstraint.length + 1;
|
|
if (holdConstraint.length > 90) this.dropBody(); //drop it if the constraint gets too long
|
|
holdConstraint.pointA = { //set constraint position
|
|
x: this.pos.x + 50 * Math.cos(this.angle), //just in front of player nose
|
|
y: this.pos.y + 50 * Math.sin(this.angle)
|
|
};
|
|
if (keys[81]) { // q = rotate the body
|
|
body[this.holdingBody].torque = 0.05 * body[this.holdingBody].mass;
|
|
}
|
|
//look for dropping held body
|
|
if (this.buttonCD < game.cycle) {
|
|
if (keys[69]) { //if holding e drops
|
|
this.holdKeyDown++;
|
|
} else if (this.holdKeyDown && !keys[69]) {
|
|
this.dropBody(); //if you hold down e long enough the body is thrown
|
|
this.throwBody();
|
|
}
|
|
}
|
|
} else if (keys[69]) { //when not holding e = pick up body
|
|
this.findClosestBody();
|
|
if (this.closest.dist < 150) { //pick up if distance closer then 100*100
|
|
this.isHolding = true;
|
|
this.holdKeyDown = 0;
|
|
this.buttonCD = game.cycle + 20;
|
|
this.holdingBody = this.closest.index; //set new body to be the holdingBody
|
|
//body[this.closest.index].isSensor = true; //sensor seems a bit inconsistant
|
|
//body[this.holdingBody].collisionFilter.group = -2; //don't collide with player
|
|
body[mech.holdingBody].collisionFilter.category = 0x000001;
|
|
body[mech.holdingBody].collisionFilter.mask = 0x000001;
|
|
body[this.holdingBody].frictionAir = 0.1; //makes the holding body less jittery
|
|
holdConstraint.bodyB = body[this.holdingBody];
|
|
holdConstraint.length = 0;
|
|
holdConstraint.pointA = {
|
|
x: this.pos.x + 50 * Math.cos(this.angle),
|
|
y: this.pos.y + 50 * Math.sin(this.angle)
|
|
};
|
|
}
|
|
}
|
|
};
|
|
this.dropBody = function () {
|
|
let timer; //reset player collision
|
|
function resetPlayerCollision() {
|
|
timer = setTimeout(function () {
|
|
const dx = mech.pos.x - body[mech.holdingBody].position.x
|
|
const dy = mech.pos.y - body[mech.holdingBody].position.y
|
|
if (dx * dx + dy * dy > 15000) {
|
|
body[mech.holdingBody].collisionFilter.category = 0x000001;
|
|
body[mech.holdingBody].collisionFilter.mask = 0x001101;
|
|
//body[mech.holdingBody].collisionFilter.group = 2; //can collide with player
|
|
} else {
|
|
resetPlayerCollision();
|
|
}
|
|
}, 100);
|
|
}
|
|
resetPlayerCollision();
|
|
this.isHolding = false;
|
|
body[this.holdingBody].frictionAir = 0.01;
|
|
holdConstraint.bodyB = jumpSensor; //set on sensor to get the constaint on somethign else
|
|
};
|
|
this.throwMax = 150;
|
|
this.throwBody = function () {
|
|
let throwMag = 0;
|
|
if (this.holdKeyDown > 20) {
|
|
if (this.holdKeyDown > this.throwMax) this.holdKeyDown = this.throwMax;
|
|
//scale fire with mass and with holdKeyDown time
|
|
throwMag = body[this.holdingBody].mass * this.holdKeyDown * 0.001;
|
|
}
|
|
body[this.holdingBody].force.x = throwMag * Math.cos(this.angle);
|
|
body[this.holdingBody].force.y = throwMag * Math.sin(this.angle);
|
|
};
|
|
this.isHolding = false;
|
|
this.holdingBody = 0;
|
|
this.closest = {
|
|
dist: 1000,
|
|
index: 0
|
|
};
|
|
this.lookingAtMob = function (mob, threshold) {
|
|
//calculate a vector from mob to player and make it length 1
|
|
const diff = Matter.Vector.normalise(Matter.Vector.sub(mob.position, player.position));
|
|
const dir = { //make a vector for the player's direction of length 1
|
|
x: Math.cos(mech.angle),
|
|
y: Math.sin(mech.angle)
|
|
}
|
|
//the dot prodcut of diff and dir will return how much over lap between the vectors
|
|
const dot = Matter.Vector.dot(dir, diff);
|
|
if (dot > threshold) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
this.lookingAt = function (i) {
|
|
//calculate a vector from mob to player and make it length 1
|
|
const diff = Matter.Vector.normalise(Matter.Vector.sub(body[i].position, player.position));
|
|
const dir = { //make a vector for the player's direction of length 1
|
|
x: Math.cos(mech.angle),
|
|
y: Math.sin(mech.angle)
|
|
}
|
|
//the dot prodcut of diff and dir will return how much over lap between the vectors
|
|
const dot = Matter.Vector.dot(dir, diff);
|
|
//console.log(dot);
|
|
if (dot > 0.9) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
this.findClosestBody = function () {
|
|
let mag = 100000;
|
|
let index = 0;
|
|
for (let i = 0; i < body.length; i++) {
|
|
let isLooking = this.lookingAt(i);
|
|
let collisionM = Matter.Query.ray(map, body[i].position, this.pos)
|
|
//let collisionB = Matter.Query.ray(body, body[i].position, this.pos)
|
|
if (collisionM.length) isLooking = false;
|
|
//magnitude of the distance between the poistion vectors of player and each body
|
|
const dist = Matter.Vector.magnitude(Matter.Vector.sub(body[i].position, this.pos));
|
|
if (dist < mag && body[i].mass < player.mass && isLooking && !body[i].eaten) {
|
|
mag = dist;
|
|
index = i;
|
|
}
|
|
}
|
|
this.closest.dist = mag;
|
|
this.closest.index = index;
|
|
};
|
|
this.exit = function () {
|
|
game.nextLevel();
|
|
window.location.reload(false);
|
|
}
|
|
this.standingOnActions = function () {
|
|
if (this.onBody.type === 'map') {
|
|
var that = this; //brings the thisness of the player deeper into the actions object
|
|
var actions = {
|
|
'death': function () {
|
|
that.death();
|
|
},
|
|
'exit': function () {
|
|
that.exit();
|
|
},
|
|
'slow': function () {
|
|
Matter.Body.setVelocity(player, { //reduce player velocity every cycle until not true
|
|
x: player.velocity.x * 0.5,
|
|
y: player.velocity.y * 0.5
|
|
});
|
|
},
|
|
'launch': function () {
|
|
//that.dropBody();
|
|
Matter.Body.setVelocity(player, { //zero player velocity for consistant jumps
|
|
x: player.velocity.x,
|
|
y: 0
|
|
});
|
|
player.force.y = -0.1 * that.mass / game.delta;
|
|
},
|
|
'default': function () {}
|
|
};
|
|
(actions[map[this.onBody.index].action] || actions['default'])();
|
|
}
|
|
}
|
|
this.drawLeg = function (stroke) {
|
|
ctx.save();
|
|
ctx.scale(this.flipLegs, 1); //leg lines
|
|
ctx.strokeStyle = stroke;
|
|
ctx.lineWidth = 7;
|
|
ctx.beginPath();
|
|
ctx.moveTo(this.hip.x, this.hip.y);
|
|
ctx.lineTo(this.knee.x, this.knee.y);
|
|
ctx.lineTo(this.foot.x, this.foot.y);
|
|
ctx.stroke();
|
|
//toe lines
|
|
ctx.lineWidth = 4;
|
|
ctx.beginPath();
|
|
ctx.moveTo(this.foot.x, this.foot.y);
|
|
ctx.lineTo(this.foot.x - 15, this.foot.y + 5);
|
|
ctx.moveTo(this.foot.x, this.foot.y);
|
|
ctx.lineTo(this.foot.x + 15, this.foot.y + 5);
|
|
ctx.stroke();
|
|
//hip joint
|
|
ctx.strokeStyle = '#333';
|
|
ctx.fillStyle = this.fillColor;
|
|
ctx.lineWidth = 2;
|
|
ctx.beginPath();
|
|
ctx.arc(this.hip.x, this.hip.y, 11, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
ctx.stroke();
|
|
//knee joint
|
|
ctx.beginPath();
|
|
ctx.arc(this.knee.x, this.knee.y, 7, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
ctx.stroke();
|
|
//foot joint
|
|
ctx.beginPath();
|
|
ctx.arc(this.foot.x, this.foot.y, 6, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
ctx.stroke();
|
|
ctx.restore();
|
|
};
|
|
this.calcLeg = function (cycle_offset, offset) {
|
|
this.hip.x = 12 + offset;
|
|
this.hip.y = 24 + offset;
|
|
//stepSize goes to zero if Vx is zero or not on ground (make this transition cleaner)
|
|
this.stepSize = 0.9 * this.stepSize + 0.1 * (8 * Math.sqrt(Math.abs(this.Vx)) * this.onGround);
|
|
//changes to stepsize are smoothed by adding only a percent of the new value each cycle
|
|
const stepAngle = 0.037 * this.walk_cycle + cycle_offset;
|
|
this.foot.x = 2 * this.stepSize * Math.cos(stepAngle) + offset;
|
|
this.foot.y = offset + this.stepSize * Math.sin(stepAngle) + this.yOff + this.height;
|
|
const Ymax = this.yOff + this.height;
|
|
if (this.foot.y > Ymax) this.foot.y = Ymax;
|
|
|
|
//calculate knee position as intersection of circle from hip and foot
|
|
const d = Math.sqrt((this.hip.x - this.foot.x) * (this.hip.x - this.foot.x) +
|
|
(this.hip.y - this.foot.y) * (this.hip.y - this.foot.y));
|
|
const l = (this.legLength1 * this.legLength1 - this.legLength2 * this.legLength2 + d * d) / (2 * d);
|
|
const h = Math.sqrt(this.legLength1 * this.legLength1 - l * l);
|
|
this.knee.x = l / d * (this.foot.x - this.hip.x) - h / d * (this.foot.y - this.hip.y) + this.hip.x + offset;
|
|
this.knee.y = l / d * (this.foot.y - this.hip.y) + h / d * (this.foot.x - this.hip.x) + this.hip.y;
|
|
};
|
|
this.draw = function () {
|
|
ctx.fillStyle = this.fillColor;
|
|
if (this.mouse.x > canvas.width / 2) {
|
|
this.flipLegs = 1;
|
|
} else {
|
|
this.flipLegs = -1;
|
|
}
|
|
this.walk_cycle += this.flipLegs * this.Vx;
|
|
|
|
//draw body
|
|
ctx.save();
|
|
ctx.translate(this.pos.x, this.pos.y);
|
|
this.calcLeg(Math.PI, -3);
|
|
this.drawLeg('#444');
|
|
this.calcLeg(0, 0);
|
|
this.drawLeg('#333');
|
|
ctx.rotate(this.angle);
|
|
ctx.strokeStyle = '#333';
|
|
ctx.lineWidth = 2;
|
|
//ctx.fillStyle = this.fillColor;
|
|
let grd = ctx.createLinearGradient(-30, 0, 30, 0);
|
|
grd.addColorStop(0, this.fillColorDark);
|
|
grd.addColorStop(1, this.fillColor);
|
|
ctx.fillStyle = grd;
|
|
ctx.beginPath();
|
|
//ctx.moveTo(0, 0);
|
|
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
|
|
ctx.arc(15, 0, 4, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
ctx.stroke();
|
|
ctx.restore();
|
|
|
|
//draw holding graphics
|
|
if (this.isHolding) {
|
|
if (this.holdKeyDown > 20) {
|
|
if (this.holdKeyDown > this.throwMax) {
|
|
ctx.strokeStyle = 'rgba(255, 0, 255, 0.8)';
|
|
} else {
|
|
ctx.strokeStyle = 'rgba(255, 0, 255, ' + (0.2 + 0.4 * this.holdKeyDown / this.throwMax) + ')';
|
|
}
|
|
} else {
|
|
ctx.strokeStyle = 'rgba(0, 255, 255, 0.2)';
|
|
}
|
|
ctx.lineWidth = 10;
|
|
ctx.beginPath();
|
|
ctx.moveTo(holdConstraint.bodyB.position.x + Math.random() * 2,
|
|
holdConstraint.bodyB.position.y + Math.random() * 2);
|
|
ctx.lineTo(this.pos.x + 15 * Math.cos(this.angle), this.pos.y + 15 * Math.sin(this.angle));
|
|
//ctx.lineTo(holdConstraint.pointA.x,holdConstraint.pointA.y);
|
|
ctx.stroke();
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
//global player variables
|
|
let player, jumpSensor, playerBody, playerHead, headSensor;
|
|
|
|
// player Object Prototype *********************************************
|
|
const mechProto = function () {
|
|
this.spawn = function () {
|
|
//player as a series of vertices
|
|
let vector = Vertices.fromPath('0 40 0 115 20 130 30 130 50 115 50 40');
|
|
playerBody = Matter.Bodies.fromVertices(0, 0, vector);
|
|
//this sensor check if the player is on the ground to enable jumping
|
|
jumpSensor = Bodies.rectangle(0, 46, 36, 6, {
|
|
sleepThreshold: 99999999999,
|
|
isSensor: true,
|
|
});
|
|
//this part of the player lowers on crouch
|
|
vector = Vertices.fromPath('0 -66 18 -82 0 -37 50 -37 50 -66 32 -82');
|
|
playerHead = Matter.Bodies.fromVertices(0, -55, vector);
|
|
//a part of player that senses if the player's head is empty and can return after crouching
|
|
headSensor = Bodies.rectangle(0, -57, 48, 45, {
|
|
sleepThreshold: 99999999999,
|
|
isSensor: true,
|
|
});
|
|
player = Body.create({ //combine jumpSensor and playerBody
|
|
parts: [playerBody, playerHead, jumpSensor, headSensor],
|
|
inertia: Infinity, //prevents player rotation
|
|
friction: 0.002,
|
|
//frictionStatic: 0.5,
|
|
restitution: 0.3,
|
|
sleepThreshold: Infinity,
|
|
collisionFilter: {
|
|
group: 0,
|
|
category: 0x001000,
|
|
mask: 0x010001
|
|
},
|
|
});
|
|
//Matter.Body.setPosition(player, mech.spawnPos);
|
|
//Matter.Body.setVelocity(player, mech.spawnVel);
|
|
Matter.Body.setMass(player, mech.mass);
|
|
World.add(engine.world, [player]);
|
|
//holding body constraint
|
|
const holdConstraint = Constraint.create({
|
|
pointA: {
|
|
x: 0,
|
|
y: 0
|
|
},
|
|
//setting constaint to jump sensor because it has to be on something until the player picks up things
|
|
bodyB: jumpSensor,
|
|
stiffness: 0.4,
|
|
});
|
|
World.add(engine.world, holdConstraint);
|
|
};
|
|
this.width = 50;
|
|
this.radius = 30;
|
|
this.fillColor = '#fff';
|
|
this.fillColorDark = '#ddd';
|
|
// const hue = '353';
|
|
// const sat = '100';
|
|
// const lit = '90';
|
|
// this.fillColor = 'hsl('+hue+','+sat+'%,'+lit+'%)';
|
|
// this.fillColorDark = 'hsl('+hue+','+(sat-10)+'%,'+(lit-10)+'%)';
|
|
// this.guns = {
|
|
// machine: {
|
|
// key: 49,
|
|
// ammo: infinite,
|
|
// isActive: true,
|
|
// isAvailable: true,
|
|
// },
|
|
// needle: {
|
|
// key: 50,
|
|
// ammo: 10,
|
|
// isActive: false,
|
|
// isAvailable: true,
|
|
// },
|
|
// shot: {
|
|
// key: 51,
|
|
// ammo: 10,
|
|
// isActive: false,
|
|
// isAvailable: true,
|
|
// }
|
|
// }
|
|
this.fireCDcycle = 0;
|
|
this.gun = 'machine'; //current gun in use
|
|
this.gunOptions = { //keeps track of keys that switch guns (used in the onkeypress event)
|
|
49: 'machine',
|
|
50: 'needle',
|
|
51: 'shot',
|
|
52: 'rail',
|
|
53: 'cannon',
|
|
54: 'super',
|
|
55: 'lob',
|
|
// 55: 'spiritBomb',
|
|
// 56: 'experimental'
|
|
}
|
|
this.height = 42;
|
|
this.yOffWhen = {
|
|
crouch: 22,
|
|
stand: 49,
|
|
jump: 70
|
|
}
|
|
this.yOff = 70;
|
|
this.yOffGoal = 70;
|
|
this.onGround = false; //checks if on ground or in air
|
|
this.onBody = {
|
|
id: 0,
|
|
index: 0,
|
|
type: "map",
|
|
action: ''
|
|
};
|
|
this.numTouching = 0;
|
|
this.crouch = false;
|
|
this.isHeadClear = true;
|
|
this.spawnPos = {
|
|
x: 0,
|
|
y: 0
|
|
};
|
|
this.spawnVel = {
|
|
x: 0,
|
|
y: 0
|
|
};
|
|
this.pos = {
|
|
x: this.spawnPos.x,
|
|
y: this.spawnPos.y
|
|
};
|
|
this.setPosToSpawn = function (xPos, yPos) {
|
|
this.spawnPos.x = xPos
|
|
this.spawnPos.y = yPos
|
|
this.pos.x = xPos;
|
|
this.pos.y = yPos;
|
|
this.Vx = this.spawnVel.x;
|
|
this.Vy = this.spawnVel.y;
|
|
Matter.Body.setPosition(player, this.spawnPos);
|
|
Matter.Body.setVelocity(player, this.spawnVel);
|
|
};
|
|
this.Sy = this.pos.y; //adds a smoothing effect to vertical only
|
|
this.Vx = 0;
|
|
this.Vy = 0;
|
|
this.VxMax = 7;
|
|
this.mass = 5;
|
|
this.Fx = 0.004 * this.mass; //run Force on ground
|
|
this.FxAir = 0.0006 * this.mass; //run Force in Air
|
|
this.Fy = -0.05 * this.mass; //jump Force
|
|
this.angle = 0;
|
|
this.walk_cycle = 0;
|
|
this.stepSize = 0;
|
|
this.flipLegs = -1;
|
|
this.hip = {
|
|
x: 12,
|
|
y: 24,
|
|
};
|
|
this.knee = {
|
|
x: 0,
|
|
y: 0,
|
|
x2: 0,
|
|
y2: 0
|
|
};
|
|
this.foot = {
|
|
x: 0,
|
|
y: 0
|
|
};
|
|
this.legLength1 = 55;
|
|
this.legLength2 = 45;
|
|
this.canvasX = canvas.width / 2;
|
|
this.canvasY = canvas.height / 2;
|
|
this.transX = this.canvasX - this.pos.x;
|
|
this.transY = this.canvasX - this.pos.x;
|
|
this.mouse = {
|
|
x: canvas.width / 3,
|
|
y: canvas.height
|
|
};
|
|
this.getMousePos = function (x, y) {
|
|
this.mouse.x = x;
|
|
this.mouse.y = y;
|
|
};
|
|
this.testingMoveLook = function () {
|
|
//move
|
|
this.pos.x = player.position.x;
|
|
this.pos.y = playerBody.position.y - this.yOff;
|
|
this.Vx = player.velocity.x;
|
|
this.Vy = player.velocity.y;
|
|
//look
|
|
this.canvasX = canvas.width / 2
|
|
this.canvasY = canvas.height / 2
|
|
this.transX = this.canvasX - this.pos.x;
|
|
this.transY = this.canvasY - this.pos.y;
|
|
this.angle = Math.atan2(this.mouse.y - this.canvasY, this.mouse.x - this.canvasX);
|
|
}
|
|
|
|
this.move = function () {
|
|
this.pos.x = player.position.x;
|
|
this.pos.y = playerBody.position.y - this.yOff;
|
|
this.Vx = player.velocity.x;
|
|
this.Vy = player.velocity.y;
|
|
};
|
|
this.look = function () {
|
|
//set a max on mouse look
|
|
let mX = this.mouse.x;
|
|
if (mX > canvas.width * 0.8) {
|
|
mX = canvas.width * 0.8;
|
|
} else if (mX < canvas.width * 0.2) {
|
|
mX = canvas.width * 0.2;
|
|
}
|
|
let mY = this.mouse.y;
|
|
if (mY > canvas.height * 0.8) {
|
|
mY = canvas.height * 0.8;
|
|
} else if (mY < canvas.height * 0.2) {
|
|
mY = canvas.height * 0.2;
|
|
}
|
|
//set mouse look
|
|
this.canvasX = this.canvasX * 0.94 + (canvas.width - mX) * 0.06;
|
|
this.canvasY = this.canvasY * 0.94 + (canvas.height - mY) * 0.06;
|
|
|
|
//set translate values
|
|
this.transX = this.canvasX - this.pos.x;
|
|
this.Sy = 0.99 * this.Sy + 0.01 * (this.pos.y);
|
|
//hard caps how behind y position tracking can get.
|
|
if (this.Sy - this.pos.y > canvas.height / 2) {
|
|
this.Sy = this.pos.y + canvas.height / 2;
|
|
} else if (this.Sy - this.pos.y < -canvas.height / 2) {
|
|
this.Sy = this.pos.y - canvas.height / 2;
|
|
}
|
|
this.transY = this.canvasY - this.Sy; //gradual y camera tracking
|
|
//this.transY = this.canvasY - this.pos.y; //normal y camera tracking
|
|
|
|
//make player head angled towards mouse
|
|
//this.angle = Math.atan2(this.mouse.y - this.canvasY, this.mouse.x - this.canvasX);
|
|
this.angle = Math.atan2(this.mouse.y + (this.Sy - this.pos.y) * game.zoom - this.canvasY, this.mouse.x - this.canvasX);
|
|
};
|
|
this.doCrouch = function () {
|
|
if (!this.crouch) {
|
|
this.crouch = true;
|
|
this.yOffGoal = this.yOffWhen.crouch;
|
|
Matter.Body.translate(playerHead, {
|
|
x: 0,
|
|
y: 40
|
|
})
|
|
}
|
|
}
|
|
this.undoCrouch = function () {
|
|
this.crouch = false;
|
|
this.yOffGoal = this.yOffWhen.stand;
|
|
Matter.Body.translate(playerHead, {
|
|
x: 0,
|
|
y: -40
|
|
})
|
|
}
|
|
this.enterAir = function () {
|
|
this.onGround = false;
|
|
player.frictionAir = 0.001;
|
|
if (this.isHeadClear) {
|
|
if (this.crouch) {
|
|
this.undoCrouch();
|
|
}
|
|
this.yOffGoal = this.yOffWhen.jump;
|
|
};
|
|
}
|
|
this.enterLand = function () {
|
|
this.onGround = true;
|
|
if (this.crouch) {
|
|
if (this.isHeadClear) {
|
|
this.undoCrouch();
|
|
player.frictionAir = 0.12;
|
|
} else {
|
|
this.yOffGoal = this.yOffWhen.crouch;
|
|
player.frictionAir = 0.5;
|
|
}
|
|
} else {
|
|
this.yOffGoal = this.yOffWhen.stand;
|
|
player.frictionAir = 0.12;
|
|
}
|
|
};
|
|
this.buttonCD_jump = 0; //cooldown for player buttons
|
|
this.keyMove = function () {
|
|
if (this.onGround) { //on ground **********************
|
|
if (this.crouch) { //crouch
|
|
if (!(keys[40] || keys[83]) && this.isHeadClear) { //not pressing crouch anymore
|
|
this.undoCrouch();
|
|
player.frictionAir = 0.12;
|
|
}
|
|
} else if (keys[40] || keys[83]) { //on ground && not crouched and pressing s or down
|
|
this.doCrouch();
|
|
player.frictionAir = 0.5;
|
|
} else if ((keys[32] || keys[38] || keys[87]) && this.buttonCD_jump + 20 < game.cycle) { //jump
|
|
this.buttonCD_jump = game.cycle; //can't jump until 20 cycles pass
|
|
Matter.Body.setVelocity(player, { //zero player velocity for consistant jumps
|
|
x: player.velocity.x,
|
|
y: 0
|
|
});
|
|
player.force.y = this.Fy / game.delta; //jump force / delta so that force is the same on game slowdowns
|
|
}
|
|
//horizontal move on ground
|
|
if (keys[37] || keys[65]) { //left or a
|
|
if (player.velocity.x > -this.VxMax) {
|
|
player.force.x += -this.Fx / game.delta;
|
|
}
|
|
} else if (keys[39] || keys[68]) { //right or d
|
|
if (player.velocity.x < this.VxMax) {
|
|
player.force.x += this.Fx / game.delta;
|
|
}
|
|
}
|
|
|
|
} else { // in air **********************************
|
|
//check for short jumps
|
|
if (this.buttonCD_jump + 60 > game.cycle && //just pressed jump
|
|
!(keys[32] || keys[38] || keys[87]) && //but not pressing jump key
|
|
this.Vy < 0) { // and velocity is up
|
|
Matter.Body.setVelocity(player, { //reduce player velocity every cycle until not true
|
|
x: player.velocity.x,
|
|
y: player.velocity.y * 0.94
|
|
});
|
|
}
|
|
if (keys[37] || keys[65]) { // move player left / a
|
|
if (player.velocity.x > -this.VxMax + 2) {
|
|
player.force.x += -this.FxAir / game.delta;
|
|
}
|
|
} else if (keys[39] || keys[68]) { //move player right / d
|
|
if (player.velocity.x < this.VxMax - 2) {
|
|
player.force.x += this.FxAir / game.delta;
|
|
}
|
|
}
|
|
}
|
|
//smoothly move height towards height goal ************
|
|
this.yOff = this.yOff * 0.85 + this.yOffGoal * 0.15
|
|
};
|
|
this.death = function () {
|
|
location.reload();
|
|
//Matter.Body.setPosition(player, this.spawnPos);
|
|
//Matter.Body.setVelocity(player, this.spawnVel);
|
|
//this.dropBody();
|
|
//game.zoom = 0; //zooms out all the way
|
|
//this.health = 1;
|
|
}
|
|
this.health = 1;
|
|
this.regen = function () {
|
|
if (this.health < 1 && game.cycle % 15 === 0) {
|
|
this.addHealth(0.01);
|
|
}
|
|
};
|
|
this.drawHealth = function () {
|
|
if (this.health < 1) {
|
|
ctx.fillStyle = 'rgba(100, 100, 100, 0.5)';
|
|
ctx.fillRect(this.pos.x - this.radius, this.pos.y - 50, 60, 10);
|
|
ctx.fillStyle = "#f00";
|
|
ctx.fillRect(this.pos.x - this.radius, this.pos.y - 50, 60 * this.health, 10);
|
|
}
|
|
};
|
|
this.addHealth = function (heal) {
|
|
this.health += heal;
|
|
if (this.health > 1) this.health = 1;
|
|
}
|
|
this.damage = function (dmg) {
|
|
this.health -= dmg;
|
|
if (this.health <= 0) {
|
|
this.death();
|
|
}
|
|
}
|
|
this.deathCheck = function () {
|
|
if (this.pos.y > game.fallHeight) { // if player is 4000px deep reset to spawn Position and Velocity
|
|
this.death();
|
|
}
|
|
};
|
|
this.hitMob = function (i, dmg) {
|
|
this.damage(dmg);
|
|
playSound("dmg" + Math.floor(Math.random() * 4));
|
|
//extra kick between player and mob
|
|
//this section would be better with forces but they don't work...
|
|
let angle = Math.atan2(player.position.y - mob[i].position.y, player.position.x - mob[i].position.x);
|
|
Matter.Body.setVelocity(player, {
|
|
x: player.velocity.x + 8 * Math.cos(angle),
|
|
y: player.velocity.y + 8 * Math.sin(angle)
|
|
});
|
|
Matter.Body.setVelocity(mob[i], {
|
|
x: mob[i].velocity.x - 8 * Math.cos(angle),
|
|
y: mob[i].velocity.y - 8 * Math.sin(angle)
|
|
});
|
|
}
|
|
|
|
this.buttonCD = 0; //cooldown for player buttons
|
|
this.eaten = [];
|
|
this.eat = function () {
|
|
if (keys[81] && this.buttonCD < game.cycle) {
|
|
this.buttonCD = game.cycle + 5;
|
|
this.findClosestBody();
|
|
let i = this.closest.index
|
|
if (this.closest.dist < 150) {
|
|
//draw eating
|
|
ctx.lineWidth = 10;
|
|
ctx.strokeStyle = '#5f9';
|
|
ctx.beginPath();
|
|
ctx.moveTo(this.pos.x + 15 * Math.cos(this.angle), this.pos.y + 15 * Math.sin(this.angle));
|
|
ctx.lineTo(body[i].position.x, body[i].position.y);
|
|
ctx.stroke();
|
|
//set to eaten
|
|
body[i].eaten = true;
|
|
//drop body
|
|
body[i].frictionAir = 0;
|
|
this.isHolding = false;
|
|
holdConstraint.bodyB = jumpSensor; //set on sensor to get the constraint on somethign else
|
|
//add to eaten
|
|
body[i].collisionFilter.category = 0x000000;
|
|
body[i].collisionFilter.mask = 0x000000;
|
|
//Matter.Body.setStatic(body[i], true)
|
|
Matter.Sleeping.set(body[i], true)
|
|
Matter.Body.scale(body[i], 0.5, 0.5)
|
|
Matter.Body.setVelocity(body[i], {
|
|
x: 0,
|
|
y: 0
|
|
});
|
|
Matter.Body.setAngularVelocity(body[i], 0.08) //(Math.random()*0.5-1)*0.1)
|
|
//Matter.World.remove(engine.world, body[i]);
|
|
//body.splice(i, 1);
|
|
this.eaten[this.eaten.length] = {
|
|
index: i,
|
|
cycle: game.cycle
|
|
}
|
|
}
|
|
}
|
|
//control behavior of eaten bodies
|
|
for (let j = 0; j < this.eaten.length; j++) {
|
|
const pos = {
|
|
x: this.pos.x + 60 * Math.cos((game.cycle + this.eaten[j].cycle) * 0.05),
|
|
y: this.pos.y + 30 * Math.sin((game.cycle + this.eaten[j].cycle) * 0.05),
|
|
}
|
|
Matter.Body.setPosition(body[this.eaten[j].index], pos);
|
|
//Matter.Body.setVelocity(body[this.eaten[j].index],{x:0, y:0});
|
|
|
|
}
|
|
|
|
if (keys[69] && this.buttonCD < game.cycle && this.eaten.length) {
|
|
this.buttonCD = game.cycle + 5;
|
|
let i = this.eaten[this.eaten.length - 1].index;
|
|
body[i].eaten = false;
|
|
body[i].collisionFilter.category = 0x000001;
|
|
body[i].collisionFilter.mask = 0x000101;
|
|
Matter.Body.setVelocity(body[i], {
|
|
x: 0,
|
|
y: 0
|
|
});
|
|
//Matter.Body.setStatic(body[i], false)
|
|
Matter.Sleeping.set(body[i], false)
|
|
Matter.Body.scale(body[i], 2, 2);
|
|
Matter.Body.setPosition(body[i], {
|
|
x: this.pos.x + 20 * Math.cos(this.angle),
|
|
y: this.pos.y + 20 * Math.sin(this.angle)
|
|
});
|
|
const impulse = 0.06 * body[i].mass;
|
|
const f = {
|
|
x: impulse * Math.cos(this.angle) / game.delta,
|
|
y: impulse * Math.sin(this.angle) / game.delta
|
|
}
|
|
body[i].force = f;
|
|
this.eaten.pop();
|
|
}
|
|
};
|
|
|
|
this.holdKeyDown = 0;
|
|
this.keyHold = function () { //checks for holding/dropping/picking up bodies
|
|
if (this.isHolding) {
|
|
//give the constaint more length and less stiffness if it is pulled out of position
|
|
const Dx = body[this.holdingBody].position.x - holdConstraint.pointA.x;
|
|
const Dy = body[this.holdingBody].position.y - holdConstraint.pointA.y;
|
|
holdConstraint.length = Math.sqrt(Dx * Dx + Dy * Dy) * 0.95;
|
|
holdConstraint.stiffness = -0.01 * holdConstraint.length + 1;
|
|
if (holdConstraint.length > 90) this.dropBody(); //drop it if the constraint gets too long
|
|
holdConstraint.pointA = { //set constraint position
|
|
x: this.pos.x + 50 * Math.cos(this.angle), //just in front of player nose
|
|
y: this.pos.y + 50 * Math.sin(this.angle)
|
|
};
|
|
if (keys[81]) { // q = rotate the body
|
|
body[this.holdingBody].torque = 0.05 * body[this.holdingBody].mass;
|
|
}
|
|
//look for dropping held body
|
|
if (this.buttonCD < game.cycle) {
|
|
if (keys[69]) { //if holding e drops
|
|
this.holdKeyDown++;
|
|
} else if (this.holdKeyDown && !keys[69]) {
|
|
this.dropBody(); //if you hold down e long enough the body is thrown
|
|
this.throwBody();
|
|
}
|
|
}
|
|
} else if (keys[69]) { //when not holding e = pick up body
|
|
this.findClosestBody();
|
|
if (this.closest.dist < 150) { //pick up if distance closer then 100*100
|
|
this.isHolding = true;
|
|
this.holdKeyDown = 0;
|
|
this.buttonCD = game.cycle + 20;
|
|
this.holdingBody = this.closest.index; //set new body to be the holdingBody
|
|
//body[this.closest.index].isSensor = true; //sensor seems a bit inconsistant
|
|
//body[this.holdingBody].collisionFilter.group = -2; //don't collide with player
|
|
body[mech.holdingBody].collisionFilter.category = 0x000001;
|
|
body[mech.holdingBody].collisionFilter.mask = 0x000001;
|
|
body[this.holdingBody].frictionAir = 0.1; //makes the holding body less jittery
|
|
holdConstraint.bodyB = body[this.holdingBody];
|
|
holdConstraint.length = 0;
|
|
holdConstraint.pointA = {
|
|
x: this.pos.x + 50 * Math.cos(this.angle),
|
|
y: this.pos.y + 50 * Math.sin(this.angle)
|
|
};
|
|
}
|
|
}
|
|
};
|
|
this.dropBody = function () {
|
|
let timer; //reset player collision
|
|
function resetPlayerCollision() {
|
|
timer = setTimeout(function () {
|
|
const dx = mech.pos.x - body[mech.holdingBody].position.x
|
|
const dy = mech.pos.y - body[mech.holdingBody].position.y
|
|
if (dx * dx + dy * dy > 15000) {
|
|
body[mech.holdingBody].collisionFilter.category = 0x000001;
|
|
body[mech.holdingBody].collisionFilter.mask = 0x001101;
|
|
//body[mech.holdingBody].collisionFilter.group = 2; //can collide with player
|
|
} else {
|
|
resetPlayerCollision();
|
|
}
|
|
}, 100);
|
|
}
|
|
resetPlayerCollision();
|
|
this.isHolding = false;
|
|
body[this.holdingBody].frictionAir = 0.01;
|
|
holdConstraint.bodyB = jumpSensor; //set on sensor to get the constaint on somethign else
|
|
};
|
|
this.throwMax = 150;
|
|
this.throwBody = function () {
|
|
let throwMag = 0;
|
|
if (this.holdKeyDown > 20) {
|
|
if (this.holdKeyDown > this.throwMax) this.holdKeyDown = this.throwMax;
|
|
//scale fire with mass and with holdKeyDown time
|
|
throwMag = body[this.holdingBody].mass * this.holdKeyDown * 0.001;
|
|
}
|
|
body[this.holdingBody].force.x = throwMag * Math.cos(this.angle);
|
|
body[this.holdingBody].force.y = throwMag * Math.sin(this.angle);
|
|
};
|
|
this.isHolding = false;
|
|
this.holdingBody = 0;
|
|
this.closest = {
|
|
dist: 1000,
|
|
index: 0
|
|
};
|
|
this.lookingAtMob = function (mob, threshold) {
|
|
//calculate a vector from mob to player and make it length 1
|
|
const diff = Matter.Vector.normalise(Matter.Vector.sub(mob.position, player.position));
|
|
const dir = { //make a vector for the player's direction of length 1
|
|
x: Math.cos(mech.angle),
|
|
y: Math.sin(mech.angle)
|
|
}
|
|
//the dot prodcut of diff and dir will return how much over lap between the vectors
|
|
const dot = Matter.Vector.dot(dir, diff);
|
|
if (dot > threshold) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
this.lookingAt = function (i) {
|
|
//calculate a vector from mob to player and make it length 1
|
|
const diff = Matter.Vector.normalise(Matter.Vector.sub(body[i].position, player.position));
|
|
const dir = { //make a vector for the player's direction of length 1
|
|
x: Math.cos(mech.angle),
|
|
y: Math.sin(mech.angle)
|
|
}
|
|
//the dot prodcut of diff and dir will return how much over lap between the vectors
|
|
const dot = Matter.Vector.dot(dir, diff);
|
|
//console.log(dot);
|
|
if (dot > 0.9) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
this.findClosestBody = function () {
|
|
let mag = 100000;
|
|
let index = 0;
|
|
for (let i = 0; i < body.length; i++) {
|
|
let isLooking = this.lookingAt(i);
|
|
let collisionM = Matter.Query.ray(map, body[i].position, this.pos)
|
|
//let collisionB = Matter.Query.ray(body, body[i].position, this.pos)
|
|
if (collisionM.length) isLooking = false;
|
|
//magnitude of the distance between the poistion vectors of player and each body
|
|
const dist = Matter.Vector.magnitude(Matter.Vector.sub(body[i].position, this.pos));
|
|
if (dist < mag && body[i].mass < player.mass && isLooking && !body[i].eaten) {
|
|
mag = dist;
|
|
index = i;
|
|
}
|
|
}
|
|
this.closest.dist = mag;
|
|
this.closest.index = index;
|
|
};
|
|
this.exit = function () {
|
|
game.nextLevel();
|
|
window.location.reload(false);
|
|
}
|
|
this.standingOnActions = function () {
|
|
if (this.onBody.type === 'map') {
|
|
var that = this; //brings the thisness of the player deeper into the actions object
|
|
var actions = {
|
|
'death': function () {
|
|
that.death();
|
|
},
|
|
'exit': function () {
|
|
that.exit();
|
|
},
|
|
'slow': function () {
|
|
Matter.Body.setVelocity(player, { //reduce player velocity every cycle until not true
|
|
x: player.velocity.x * 0.5,
|
|
y: player.velocity.y * 0.5
|
|
});
|
|
},
|
|
'launch': function () {
|
|
//that.dropBody();
|
|
Matter.Body.setVelocity(player, { //zero player velocity for consistant jumps
|
|
x: player.velocity.x,
|
|
y: 0
|
|
});
|
|
player.force.y = -0.1 * that.mass / game.delta;
|
|
},
|
|
'default': function () {}
|
|
};
|
|
(actions[map[this.onBody.index].action] || actions['default'])();
|
|
}
|
|
}
|
|
this.drawLeg = function (stroke) {
|
|
ctx.save();
|
|
ctx.scale(this.flipLegs, 1); //leg lines
|
|
ctx.strokeStyle = stroke;
|
|
ctx.lineWidth = 7;
|
|
ctx.beginPath();
|
|
ctx.moveTo(this.hip.x, this.hip.y);
|
|
ctx.lineTo(this.knee.x, this.knee.y);
|
|
ctx.lineTo(this.foot.x, this.foot.y);
|
|
ctx.stroke();
|
|
//toe lines
|
|
ctx.lineWidth = 4;
|
|
ctx.beginPath();
|
|
ctx.moveTo(this.foot.x, this.foot.y);
|
|
ctx.lineTo(this.foot.x - 15, this.foot.y + 5);
|
|
ctx.moveTo(this.foot.x, this.foot.y);
|
|
ctx.lineTo(this.foot.x + 15, this.foot.y + 5);
|
|
ctx.stroke();
|
|
//hip joint
|
|
ctx.strokeStyle = '#333';
|
|
ctx.fillStyle = this.fillColor;
|
|
ctx.lineWidth = 2;
|
|
ctx.beginPath();
|
|
ctx.arc(this.hip.x, this.hip.y, 11, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
ctx.stroke();
|
|
//knee joint
|
|
ctx.beginPath();
|
|
ctx.arc(this.knee.x, this.knee.y, 7, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
ctx.stroke();
|
|
//foot joint
|
|
ctx.beginPath();
|
|
ctx.arc(this.foot.x, this.foot.y, 6, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
ctx.stroke();
|
|
ctx.restore();
|
|
};
|
|
this.calcLeg = function (cycle_offset, offset) {
|
|
this.hip.x = 12 + offset;
|
|
this.hip.y = 24 + offset;
|
|
//stepSize goes to zero if Vx is zero or not on ground (make this transition cleaner)
|
|
this.stepSize = 0.9 * this.stepSize + 0.1 * (8 * Math.sqrt(Math.abs(this.Vx)) * this.onGround);
|
|
//changes to stepsize are smoothed by adding only a percent of the new value each cycle
|
|
const stepAngle = 0.037 * this.walk_cycle + cycle_offset;
|
|
this.foot.x = 2 * this.stepSize * Math.cos(stepAngle) + offset;
|
|
this.foot.y = offset + this.stepSize * Math.sin(stepAngle) + this.yOff + this.height;
|
|
const Ymax = this.yOff + this.height;
|
|
if (this.foot.y > Ymax) this.foot.y = Ymax;
|
|
|
|
//calculate knee position as intersection of circle from hip and foot
|
|
const d = Math.sqrt((this.hip.x - this.foot.x) * (this.hip.x - this.foot.x) +
|
|
(this.hip.y - this.foot.y) * (this.hip.y - this.foot.y));
|
|
const l = (this.legLength1 * this.legLength1 - this.legLength2 * this.legLength2 + d * d) / (2 * d);
|
|
const h = Math.sqrt(this.legLength1 * this.legLength1 - l * l);
|
|
this.knee.x = l / d * (this.foot.x - this.hip.x) - h / d * (this.foot.y - this.hip.y) + this.hip.x + offset;
|
|
this.knee.y = l / d * (this.foot.y - this.hip.y) + h / d * (this.foot.x - this.hip.x) + this.hip.y;
|
|
};
|
|
this.draw = function () {
|
|
ctx.fillStyle = this.fillColor;
|
|
if (this.mouse.x > canvas.width / 2) {
|
|
this.flipLegs = 1;
|
|
} else {
|
|
this.flipLegs = -1;
|
|
}
|
|
this.walk_cycle += this.flipLegs * this.Vx;
|
|
|
|
//draw body
|
|
ctx.save();
|
|
ctx.translate(this.pos.x, this.pos.y);
|
|
this.calcLeg(Math.PI, -3);
|
|
this.drawLeg('#444');
|
|
this.calcLeg(0, 0);
|
|
this.drawLeg('#333');
|
|
ctx.rotate(this.angle);
|
|
ctx.strokeStyle = '#333';
|
|
ctx.lineWidth = 2;
|
|
//ctx.fillStyle = this.fillColor;
|
|
let grd = ctx.createLinearGradient(-30, 0, 30, 0);
|
|
grd.addColorStop(0, this.fillColorDark);
|
|
grd.addColorStop(1, this.fillColor);
|
|
ctx.fillStyle = grd;
|
|
ctx.beginPath();
|
|
//ctx.moveTo(0, 0);
|
|
ctx.arc(0, 0, 30, 0, 2 * Math.PI);
|
|
ctx.arc(15, 0, 4, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
ctx.stroke();
|
|
ctx.restore();
|
|
|
|
//draw holding graphics
|
|
if (this.isHolding) {
|
|
if (this.holdKeyDown > 20) {
|
|
if (this.holdKeyDown > this.throwMax) {
|
|
ctx.strokeStyle = 'rgba(255, 0, 255, 0.8)';
|
|
} else {
|
|
ctx.strokeStyle = 'rgba(255, 0, 255, ' + (0.2 + 0.4 * this.holdKeyDown / this.throwMax) + ')';
|
|
}
|
|
} else {
|
|
ctx.strokeStyle = 'rgba(0, 255, 255, 0.2)';
|
|
}
|
|
ctx.lineWidth = 10;
|
|
ctx.beginPath();
|
|
ctx.moveTo(holdConstraint.bodyB.position.x + Math.random() * 2,
|
|
holdConstraint.bodyB.position.y + Math.random() * 2);
|
|
ctx.lineTo(this.pos.x + 15 * Math.cos(this.angle), this.pos.y + 15 * Math.sin(this.angle));
|
|
//ctx.lineTo(holdConstraint.pointA.x,holdConstraint.pointA.y);
|
|
ctx.stroke();
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
//makes the player object based on mechprototype
|
|
const mech = new mechProto(); |