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.

368 lines
11 KiB
JavaScript

2 years ago
(() => {
// 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: "./guestbook/", text: "I want to leave a trace"}, //1 - +( I want to leave a trace) +(guestbook/)
{x: 250, y: 200, url: "./backglass.gif", text: "Backglass Zine"}, //2 - + (Backglass Zine) + (backglass.gif)
{x: 250, y: 380, url: "./S12/", text: "I don't really want to know"}, //3 + (I don't really want to know) + (S12/)
{x: 100, y: 380, url: "./S4/", text: "is this real?"}, //4 - + (is this real?) + (S4/)
{x: 400, y: 380, url: "./S7/", text: "there are more than two ways"}, //5 - Camillo + there are more than two ways) + (S7/)
{x: 175, y: 470, url: "./S13/", text: "I'm gonna keep this for later"}, //6 (I'm gonna keep this for later) + (S13/)
{x: 325, y: 470, url: "./lost_in_files.jpg", text: "I want to get lost in files"}, //6 - Clara image + (I want to get lost in files) + (lost_in_files.jpg)
{x: 400, y: 200, url: "./situationists.png", text: ""}, //2 + (it seems to be a situation) + situ
];
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);
})();