You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
423 lines
13 KiB
JavaScript
423 lines
13 KiB
JavaScript
(() => {
|
|
// plugins
|
|
Matter.use(MatterAttractors);
|
|
|
|
// constants
|
|
const PATHS = {
|
|
DOME: '0 0 0 250 19 250 20 231.9 25.7 196.1 36.9 161.7 53.3 129.5 74.6 100.2 100.2 74.6 129.5 53.3 161.7 36.9 196.1 25.7 231.9 20 268.1 20 303.9 25.7 338.3 36.9 370.5 53.3 399.8 74.6 425.4 100.2 446.7 129.5 463.1 161.7 474.3 196.1 480 231.9 480 250 500 250 500 0 0 0',
|
|
DROP_LEFT: '0 0 20 0 70 100 20 150 0 150 0 0',
|
|
DROP_RIGHT: '50 0 68 0 68 150 50 150 0 100 50 0',
|
|
APRON_LEFT: '0 0 180 120 0 120 0 0',
|
|
APRON_RIGHT: '180 0 180 120 0 120 180 0'
|
|
};
|
|
|
|
const COLOR = {
|
|
BACKGROUND: '#000',
|
|
OUTER: '#000',
|
|
INNER: '#FFFFFF',
|
|
BUMPER: '#FFFFFF',
|
|
BUMPER_LIT:'#FF3F99', //Lobster
|
|
// BUMPER_LIT:'#FF8C1F', //PNF
|
|
// BUMPER_LIT:'#FFFF01', //Pigeon Plaza
|
|
// BUMPER_LIT:'#86FC06', //Secret Garden
|
|
// BUMPER_LIT:'#04D9FF', //Secret Garden
|
|
|
|
PADDLE: '#FFFFFF',
|
|
|
|
PINBALL: '#FF3F99', //Lobster
|
|
// PINBALL: '#FF8C1F', //PNF
|
|
// PINBALL: '#FFFF01', //Pigeon Plaza
|
|
// PINBALL: '#86FC06', //Secret Garden
|
|
// PINBALL: '#04D9FF', //Secret Garden
|
|
|
|
LINK: '#FF3F99', //Lobster
|
|
// LINK: '#FF8C1F', //PNF
|
|
// LINK: '#FFFF01', //Pigeon Plaza
|
|
// LINK: '#86FC06', //Secret Garden
|
|
// LINK: '#04D9FF', //Secret Garden
|
|
};
|
|
|
|
|
|
const GRAVITY = 1;
|
|
const WIREFRAMES = false;
|
|
const BUMPER_BOUNCE = 1.5;
|
|
const MAX_VELOCITY = 50;
|
|
|
|
// matter.js has a built in random range function, but it is deterministic
|
|
function rand(min, max) {
|
|
return Math.random() * (max - min) + min;
|
|
}
|
|
|
|
|
|
// shared variables
|
|
let engine, world, render, pinball, stopperGroup;
|
|
|
|
function load() {
|
|
init();
|
|
createStaticBodies();
|
|
createPinball();
|
|
createEvents();
|
|
}
|
|
|
|
function init() {
|
|
// engine (shared)
|
|
engine = Matter.Engine.create();
|
|
|
|
// world (shared)
|
|
world = engine.world;
|
|
world.bounds = {
|
|
min: { x: 0, y: 0},
|
|
max: { x: 500, y: 800 }
|
|
};
|
|
|
|
world.gravity.y = GRAVITY; // simulate rolling on a slanted table
|
|
|
|
// render (shared)
|
|
render = Matter.Render.create({
|
|
element: $('.container')[0],
|
|
engine: engine,
|
|
options: {
|
|
width: world.bounds.max.x,
|
|
height: world.bounds.max.y,
|
|
wireframes: WIREFRAMES,
|
|
background: COLOR.BACKGROUND
|
|
}
|
|
});
|
|
|
|
Matter.Render.run(render);
|
|
|
|
// runner
|
|
let runner = Matter.Runner.create();
|
|
Matter.Runner.run(runner, engine);
|
|
|
|
// used for collision filtering on various bodies
|
|
stopperGroup = Matter.Body.nextGroup(true);
|
|
|
|
// based on https://stackoverflow.com/questions/28324303/matter-js-mouse-click-on-body
|
|
// https://github.com/liabru/matter-js/blob/081645474c4aa798e5b1ede5d5230b0ecb8835d2/examples/events.js
|
|
var mouse = Matter.Mouse.create(render.canvas);
|
|
var mouseConstraint = Matter.MouseConstraint.create(engine, {
|
|
mouse: mouse,
|
|
constraint: {
|
|
stiffness: 1,
|
|
render: {
|
|
visible: false
|
|
}
|
|
}
|
|
})
|
|
|
|
Matter.World.add(world, mouseConstraint);
|
|
|
|
|
|
|
|
|
|
Matter.Events.on(engine, 'tick', function(event) {
|
|
if(mouseConstraint.mouse.button == 0){
|
|
console.log("click", mouseConstraint.body);
|
|
if (mouseConstraint.body && mouseConstraint.body.url) {
|
|
console.log("opening link", mouseConstraint.body.url);
|
|
|
|
function openTab(url) {
|
|
// Create link in memory
|
|
var a = window.document.createElement("a");
|
|
//a.target = '_blank';
|
|
a.href = url;
|
|
|
|
// Dispatch fake click
|
|
var e = window.document.createEvent("MouseEvents");
|
|
e.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
|
|
a.dispatchEvent(e);
|
|
};
|
|
|
|
openTab(mouseConstraint.body.url);
|
|
|
|
|
|
//window.open(mouseConstraint.body.url, "_self");
|
|
}
|
|
}
|
|
});
|
|
|
|
// an example of using mouse events on a mouse
|
|
Matter.Events.on(mouseConstraint, 'mousedown', function(event) {
|
|
var mousePosition = event.mouse.position;
|
|
console.log('mousedown at ' + mousePosition.x + ' ' + mousePosition.y);
|
|
});
|
|
}
|
|
|
|
|
|
|
|
|
|
//1 Placenotfound *activate the walls with it
|
|
//var BUMPER_DATA = [
|
|
// {x: 100, y: 200, url: ""}, //1 - Guest book
|
|
// {x: 250, y: 200, url: ""}, //2 - Jacopo (?)
|
|
|
|
// {x: 250, y: 380, url: ""}, //3 - Camilo
|
|
// {x: 100, y: 380, url: ""}, //4 - Camillo
|
|
// {x: 400, y: 380, url: ""}, //5 - Camillo
|
|
|
|
// {x: 175, y: 470, url: ""}, //6 - Camillo code access: WHOLE
|
|
// {x: 325, y: 470, url: ""}, //6 - Clara image
|
|
// {x: 400, y: 200, url: ""}, //2 - Floor html
|
|
//];
|
|
|
|
//2 Lobster Lounge *activate the walls with it
|
|
var BUMPER_DATA = [
|
|
{x: 250, y: 200, url: "./S3/", text: "don't ask me to leave"}, //1 - Camilo + (don't ask me to leave) + (S3/)
|
|
{x: 400, y: 200, url: "./guestbook/", text: "I want to leave a trace"}, //2 - + (I want to leave a trace) + (/guestbook/index.php)
|
|
|
|
{x: 175, y: 290, url: "./writings/", text: "we have to value materials"}, //4 (we have to value materials) + (/writings/index.html)
|
|
{x: 325, y: 290, url: "./Strolling_Cat/", text: "I just want to walk in peace"}, //5 I don't know where we are going but... + (I just want to walk in peace) + (link)
|
|
{x: 175, y: 470, url: "./IRL_ENERGY/", text: "there is so much to see."}, //6 - + there is so much to see. + /HOTSPOTS/2%20fishshop/IRL_ENERGY/index.html
|
|
{x: 325, y: 470, url: "./offline.jpg", text: "I'm taking a detour."}, //3 I don't know where we are going but... + I'm taking a detour. + /HOTSPOTS/2 fishshop/offline.jpg
|
|
|
|
{x: 100, y: 560, url: "./shirley2.jpg", text: "I just want to be in peace"}, //7 - (I just want to be in peace) + (Shirley2.jpg)
|
|
{x: 250, y: 560, url: "./iws.pdf", text: "I just want to walk in peace"}, //8 - (I just want to walk in peace) + (iws.pdf)
|
|
];
|
|
|
|
//3 The Secret Garden //6 walls *activate the walls with it
|
|
// var BUMPER_DATA = [
|
|
|
|
//{x: 100, y: 200, url: ""}, //1 - Guest book
|
|
//{x: 400, y: 200, url: "http://martinfoucaut.com"}, //2 - Jacopo (?)
|
|
|
|
//{x: 175, y: 290, url: ""}, //4 - Strolling cat (+code access: CAT)
|
|
//{x: 325, y: 290, url: ""}, //5 - Camilo
|
|
|
|
//{x: 250, y: 380, url: ""}, //6 - Camilo
|
|
|
|
//{x: 175, y: 470, url: ""}, //7 - Euna (video)
|
|
//{x: 325, y: 470, url: ""}, //8 - Euna (image)
|
|
|
|
//{x: 250, y: 560, url: ""}, //9 - Floor (+code access: pprgm)
|
|
|
|
// ];
|
|
|
|
//4 Sonic the Sellout //no wall *activate the walls with it
|
|
//var BUMPER_DATA = [
|
|
|
|
//{x: 100, y: 200, url: ""}, //1 - Naami (+code access: SAVIOR)
|
|
//{x: 250, y: 200, url: "http://federicoponi.it"}, //2 - Guest book
|
|
//{x: 400, y: 200, url: "http://martinfoucaut.com"}, //3 - Jacopo (?)
|
|
|
|
//{x: 175, y: 290, url: ""}, //4 - Strolling cat (+code access: CAT)
|
|
//{x: 325, y: 290, url: ""}, //5 - Camilo
|
|
|
|
//{x: 100, y: 380, url: ""}, //6 - Camilo
|
|
//{x: 250, y: 380, url: ""}, //7 - Euna (video)Euna (image)Jacopo (?)
|
|
//{x: 400, y: 380, url: ""}, //8 - Camilo
|
|
|
|
//{x: 175, y: 470, url: ""}, //9 - Kendal downloadable$ code access: APPOINTMENT
|
|
//{x: 325, y: 470, url: ""}, //10 - Kendal (image)
|
|
//{x: 400, y: 560, url: ""}, //11 - Kendal (image)
|
|
|
|
//{x: 100, y: 560, url: ""}, //12 - praxis zine (+code access: PRAXIS)
|
|
//{x: 250, y: 560, url: ""}, //13 - Euna (video)Euna (image)Jacopo (?)
|
|
//];
|
|
|
|
|
|
|
|
function createStaticBodies() {
|
|
Matter.World.add(world, [
|
|
// table boundaries (top, bottom, left, right)
|
|
boundary(50, -30, 500, 100),
|
|
boundary(250, 830, 500, 100),
|
|
boundary(-40, 400, 100, 800),
|
|
boundary(540, 400, 100, 800),
|
|
|
|
// dome
|
|
path(251, 10, PATHS.DOME),
|
|
|
|
//The secret garden
|
|
// wall(250, 200, 30, 110, COLOR.INNER),
|
|
// wall(100, 380, 30, 110, COLOR.INNER),
|
|
// wall(100, 380, 30, 110, COLOR.INNER),
|
|
// wall(400, 380, 30, 110, COLOR.INNER),
|
|
// wall(130, 560, 30, 110, COLOR.INNER, -0.96),
|
|
// wall(370, 560, 30, 110, COLOR.INNER, 0.96),
|
|
|
|
//Sonic the Sellout
|
|
|
|
// Pigeons Plaza
|
|
//wall(250, 380, 30, 110, COLOR.INNER, 0.96),
|
|
|
|
// PNF
|
|
//wall(250, 560, 30, 110, COLOR.INNER),
|
|
//wall(175, 290, 30, 110, COLOR.INNER),
|
|
//wall(325, 290, 30, 110, COLOR.INNER),
|
|
//wall(130, 560, 30, 110, COLOR.INNER, -0.96),
|
|
//wall(370, 560, 30, 110, COLOR.INNER, 0.96),
|
|
|
|
// Lobster Lounge
|
|
wall(455, 520, 7, 560, COLOR.OUTER),
|
|
wall(250, 380, 30, 110, COLOR.INNER),
|
|
wall(100, 380, 30, 110, COLOR.INNER),
|
|
wall(400, 380, 30, 110, COLOR.INNER),
|
|
wall(130, 200, 30, 110, COLOR.INNER, -0.96),
|
|
wall(370, 560, 30, 110, COLOR.INNER, 0.96),
|
|
|
|
// reset zones (center, right)
|
|
reset(221, 420),
|
|
reset(470, 40)
|
|
]);
|
|
|
|
BUMPER_DATA.forEach(d => {
|
|
Matter.World.add(world, bumper(d.x, d.y, d.url));
|
|
});
|
|
|
|
//
|
|
// eventually
|
|
// Matter.World.add(world, bumper(d)); and change bumper to use b.whatever as needed (incl color for instance)
|
|
}
|
|
|
|
|
|
|
|
function createPinball() {
|
|
// x/y are set to when pinball is launched
|
|
pinball = Matter.Bodies.circle(0, 0, 12, {
|
|
label: 'pinball',
|
|
collisionFilter: {
|
|
group: stopperGroup
|
|
},
|
|
render: {
|
|
fillStyle: COLOR.PINBALL
|
|
}
|
|
});
|
|
Matter.World.add(world, pinball);
|
|
launchPinball();
|
|
}
|
|
|
|
function createEvents() {
|
|
// events for when the pinball hits stuff
|
|
Matter.Events.on(engine, 'collisionStart', function(event) {
|
|
let pairs = event.pairs;
|
|
pairs.forEach(function(pair) {
|
|
if (pair.bodyB.label === 'pinball') {
|
|
switch (pair.bodyA.label) {
|
|
case 'reset':
|
|
launchPinball();
|
|
break;
|
|
case 'bumper':
|
|
pingBumper(pair.bodyA);
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
// regulate pinball
|
|
Matter.Events.on(engine, 'beforeUpdate', function(event) {
|
|
// bumpers can quickly multiply velocity, so keep that in check
|
|
Matter.Body.setVelocity(pinball, {
|
|
x: Math.max(Math.min(pinball.velocity.x, MAX_VELOCITY), -MAX_VELOCITY),
|
|
y: Math.max(Math.min(pinball.velocity.y, MAX_VELOCITY), -MAX_VELOCITY),
|
|
});
|
|
|
|
// cheap way to keep ball from going back down the shooter lane
|
|
if (pinball.position.x > 450 && pinball.velocity.y > 0) {
|
|
Matter.Body.setVelocity(pinball, { x: 0, y: -10 });
|
|
}
|
|
});
|
|
|
|
}
|
|
|
|
function launchPinball() {
|
|
Matter.Body.setPosition(pinball, { x: 465, y: 765 });
|
|
Matter.Body.setVelocity(pinball, { x: 0, y: -25 + rand(-2, 2) });
|
|
Matter.Body.setAngularVelocity(pinball, 0);
|
|
}
|
|
|
|
function pingBumper(bumper) {
|
|
//updateScore(currentScore + 10);
|
|
|
|
// flash color
|
|
bumper.render.fillStyle = COLOR.BUMPER_LIT;
|
|
setTimeout(function() {
|
|
bumper.render.fillStyle = COLOR.BUMPER;
|
|
}, 100);
|
|
|
|
// swap text (#textfeature)
|
|
var textcontainer = document.getElementById("but");
|
|
console.log('BUMPER:', bumper)
|
|
BUMPER_DATA.forEach(b => {
|
|
if (b.url == bumper.url){
|
|
console.log('b.url == bumper.url :', b.url, bumper.url);
|
|
console.log('b.text:', b.text);
|
|
textcontainer.innerText = b.text;
|
|
}
|
|
|
|
});
|
|
}
|
|
|
|
|
|
|
|
// outer edges of pinball table
|
|
function boundary(x, y, width, height) {
|
|
return Matter.Bodies.rectangle(x, y, width, height, {
|
|
isStatic: true,
|
|
render: {
|
|
fillStyle: COLOR.OUTER
|
|
}
|
|
});
|
|
}
|
|
|
|
// wall segments
|
|
function wall(x, y, width, height, color, angle = 0) {
|
|
return Matter.Bodies.rectangle(x, y, width, height, {
|
|
angle: angle,
|
|
isStatic: true,
|
|
chamfer: { radius: 15 },
|
|
render: {
|
|
fillStyle: color
|
|
}
|
|
});
|
|
}
|
|
|
|
// bodies created from SVG paths
|
|
function path(x, y, path) {
|
|
let vertices = Matter.Vertices.fromPath(path);
|
|
return Matter.Bodies.fromVertices(x, y, vertices, {
|
|
isStatic: true,
|
|
render: {
|
|
fillStyle: COLOR.OUTER,
|
|
|
|
// add stroke and line width to fill in slight gaps between fragments
|
|
strokeStyle: COLOR.OUTER,
|
|
lineWidth: 1
|
|
}
|
|
});
|
|
}
|
|
|
|
// round bodies that repel pinball
|
|
function bumper(x, y, url) {
|
|
let bumper = Matter.Bodies.circle(x, y, 25, {
|
|
label: 'bumper',
|
|
url: url,
|
|
isStatic: true,
|
|
render: {
|
|
fillStyle: COLOR.BUMPER
|
|
}
|
|
});
|
|
|
|
// for some reason, restitution is reset unless it's set after body creation
|
|
bumper.restitution = BUMPER_BOUNCE;
|
|
|
|
return bumper;
|
|
}
|
|
|
|
// invisible bodies to constrict paddles
|
|
|
|
|
|
// contact with these bodies causes pinball to be relaunched
|
|
function reset(x, width) {
|
|
return Matter.Bodies.rectangle(x, 781, width, 4, {
|
|
label: 'reset',
|
|
isStatic: true,
|
|
render: {
|
|
fillStyle: '#00070C'
|
|
}
|
|
});
|
|
}
|
|
|
|
window.addEventListener('load', load, false);
|
|
})(); |