xxxxxxxxxx
707
//reference material
// https://youtu.be/fQRakGkGudI
// https://twitter.com/i/status/1710864952137769155
// music from https://www.hamienet.com/midi48217_xi--Freedom-Dive.html
// dont click the tidy code button it will crash ur pc
let soundFiles = {bell: []}
const NUM_PITCHES = 32; // Total number of pitch files (excluding the middle C)
const MIDDLE_C_INDEX = 10; // Assuming pitch_0.mp3 is the middle C
// Constants
const PEG_SIZE = 13;
const BALL_RADIUS = 7;
const BUCKET_INCREMENTS = [0.2, 0.2, 0.2, 2, 4, 8, 16, 32, 1000, 5000];
var BUCKET_WIDTH = 50;
const BUCKET_HEIGHT = 60;
const NUDGE_FORCE = 0.005; // Adjust as needed
const ballCategory = 0x0002;
const defaultCategory = 0x0001;
const MAX_X_VELOCITY = 1.1; // Adjust this value as needed
const TOP_MARGIN = 65; // Space at the top
const BOTTOM_MARGIN = 100; // Space at the bottom
const PEG_SPACING = 42; // Closer pegs
const PEG_ROWS = 21; // More rows
// will crash at higher values of 17 because there wont be a sound file for it
const NUM_BUCKETS = 17; // Adjust as needed
const MAX_BALLS = 512;
const MOVE_THRESHOLD = 2000; // 2 seconds
const light_radius = 5;
const maxValue = 18;
const minValue = 0.2;
const centerBucketIndex = (NUM_BUCKETS - 1) / 2;
const maxDistanceFromCenter = Math.abs(NUM_BUCKETS - 1 - centerBucketIndex);
const LogBalls = [];
const logEntries = [];
var ballcost = 3000;
var t = 0;
var score = 0;
var money = 3842469.65;
var balls_spawned = 0;
var profit_per_ball = 0;
var profit = 0;
let exportButton;
var autobuy; //checkbox
// Matter.js module aliases
let Engine = Matter.Engine,
World = Matter.World,
Bodies = Matter.Bodies,
Body = Matter.Body,
Events = Matter.Events;
let engine, world;
let buckets = [];
let pegs = [];
let balls = [];
let scores = Array(NUM_BUCKETS).fill(0);
var debug;
var volumeSlider;
const sounds = new Map();
const songs = [
[[18, 100], [15, 100], [10, 100], [6, 100], [18, 100], [10, 100], [13, 100], [18, 100], [10, 100], [22, 100], [13, 100], [18, 100], [22, 100], [25, 100], [13, 100], [18, 100], [22, 100], [25, 100], [28, 100], [18, 100], [22, 100], [25, 100], [28, 100], [18, 100], [30, 100], [22, 100], [25, 100], [30, 100], [22, 100], [25, 100], [30, 100], [25, 100], [30, 100], [30, 100], [30, 100], [27, 100], [23, 100], [30, 100], [27, 100], [23, 100], [30, 100], [27, 100], [23, 100], [28, 100], [23, 100], [18, 100], [28, 100], [23, 100], [28, 100], [18, 100], [27, 100], [23, 100], [18, 100], [27, 100], [23, 100], [18, 100], [27, 100], [23, 100], [18, 100], [22, 100], [18, 100], [13, 100], [22, 100], [18, 100], [22, 100], [13, 100], [25, 100], [20, 100], [15, 100], [25, 100], [20, 100], [15, 100], [25, 100], [20, 100], [15, 100], [23, 100], [20, 100], [15, 100], [23, 100], [20, 100], [23, 100], [15, 100], [22, 100], [20, 100], [15, 100], [22, 100], [20, 100], [15, 100], [23, 100], [15, 100], [25, 100], [15, 100], [27, 100], [18, 100], [28, 100], [18, 100], [30, 100], [18, 100], [28, 100], [23, 100], [28, 100], [23, 100], [28, 100], [23, 100], [28, 100], [23, 100], [28, 100], [28, 100], [28, 100], [28, 100], [23, 100], [28, 100], [23, 100], [27, 100], [22, 100], [30, 100], [27, 100], [30, 100], [22, 100], [29, 100], [27, 100], [29, 100], [22, 100], [27, 100], [22, 100], [30, 100], [27, 100], [23, 100], [30, 100], [27, 100], [23, 100], [30, 100], [27, 100], [23, 100], [27, 100], [23, 100], [20, 100], [27, 100], [23, 100], [27, 100], [20, 100], [28, 100], [23, 100], [28, 100], [20, 100], [30, 100], [23, 100], [30, 100], [20, 100], [28, 100], [23, 100], [28, 100], [23, 100], [28, 100], [23, 100], [30, 100], [27, 100], [22, 100], [30, 100], [27, 100], [22, 100], [28, 100], [22, 100], [27, 100], [22, 100], [27, 100], [19, 100], [31, 100], [27, 100], [31, 100], [22, 100], [27, 100], [22, 100], [27, 100], [22, 100], [27, 100], [19, 100], [27, 100], [27, 100], [30, 100], [27, 100], [30, 100], [27, 100], [27, 100], [27, 100], [23, 100], [23, 100], [28, 100], [30, 100], [30, 100], [30, 100], [30, 100], [30, 100], [30, 100], [30, 100], [22, 100], [6, 100], [10, 100], [13, 100], [18, 100], [10, 100], [22, 100], [13, 100], [18, 100], [22, 100], [25, 100], [13, 100], [18, 100], [22, 100], [25, 100], [28, 100], [18, 100], [22, 100], [25, 100], [28, 100], [30, 100], [18, 100], [22, 100], [25, 100], [30, 100], [22, 100], [25, 100], [30, 100], [25, 100], [30, 100], [30, 100], [27, 100], [23, 100], [30, 100], [18, 100], [27, 100], [15, 100], [30, 100], [23, 100], [28, 100], [18, 100], [15, 100], [27, 100], [30, 100], [13, 100], [28, 100], [25, 100], [22, 100], [27, 100], [18, 100], [23, 100], [13, 100], [25, 100], [22, 100], [19, 100], [22, 100], [23, 100], [27, 100], [20, 100], [15, 100], [27, 100], [23, 100], [20, 100], [15, 100], [23, 100], [20, 100], [15, 100], [22, 100], [18, 100], [23, 100], [15, 100], [18, 100], [22, 100], [30, 100], [15, 100], [18, 100], [27, 100], [30, 100], [15, 100], [28, 100], [16, 100], [23, 100], [30, 100], [20, 100], [16, 100], [23, 100], [20, 100], [28, 100], [16, 100], [23, 100], [27, 100], [30, 100], [18, 100], [15, 100], [23, 100], [18, 100], [30, 100], [15, 100], [23, 100], [27, 100], [30, 100], [16, 100], [18, 100], [20, 100], [22, 100], [23, 100], [28, 100], [20, 100], [16, 100], [27, 100], [30, 100], [23, 100], [28, 100], [20, 100], [16, 100], [23, 100], [20, 100], [25, 100], [22, 100], [18, 100], [25, 100], [13, 100], [18, 100], [22, 100], [25, 100], [30, 100], [28, 100], [27, 100], [25, 100], [30, 100], [23, 100], [27, 100], [18, 100], [27, 100], [30, 100], [15, 100], [23, 100], [28, 100], [18, 100], [15, 100], [27, 100], [30, 100], [25, 100], [28, 100], [13, 100], [22, 100], [27, 100], [23, 100], [18, 100], [13, 100], [22, 100], [25, 100], [13, 100], [19, 100], [22, 100], [25, 100], [22, 100], [27, 100], [19, 100], [25, 100], [23, 100], [22, 100], [20, 100], [23, 100], [27, 100], [15, 100], [23, 100], [27, 100], [20, 100], [15, 100], [23, 100], [20, 100], [15, 100], [22, 100], [18, 100], [15, 100], [22, 100], [18, 100], [15, 100], [30, 100], [18, 100], [22, 100], [30, 100], [27, 100], [25, 100], [23, 100], [22, 100], [28, 100], [16, 100], [23, 100], [30, 100], [20, 100], [16, 100], [23, 100], [20, 100], [28, 100], [16, 100], [23, 100], [30, 100], [18, 100], [27, 100], [15, 100], [23, 100], [18, 100], [15, 100], [30, 100], [23, 100], [30, 100], [27, 100], [16, 100], [18, 100], [20, 100], [22, 100], [28, 100], [16, 100], [18, 100], [30, 100], [20, 100], [22, 100], [23, 100], [28, 100], [16, 100], [20, 100], [23, 100], [25, 100], [18, 100], [30, 100], [22, 100], [25, 100], [30, 100], [18, 100], [22, 100], [28, 100], [25, 100], [30, 100], [27, 100], [28, 100], [25, 100], [27, 100], [25, 100], [30, 100], [27, 100], [23, 100], [30, 100], [27, 100], [23, 100], [30, 100], [27, 100], [23, 100], [28, 100], [23, 100], [18, 100], [28, 100], [23, 100], [28, 100], [18, 100], [27, 100], [23, 100], [18, 100], [27, 100], [23, 100], [18, 100], [27, 100], [23, 100], [18, 100], [22, 100], [30, 100], [18, 100], [30, 100], [22, 100], [18, 100], [22, 100], [30, 100], [25, 100], [20, 100], [15, 100], [25, 100], [20, 100], [15, 100], [25, 100], [20, 100], [15, 100], [23, 100], [20, 100], [15, 100], [23, 100], [20, 100], [23, 100], [15, 100], [30, 100], [22, 100], [20, 100], [15, 100], [30, 100], [22, 100], [20, 100], [15, 100], [30, 100], [23, 100], [15, 100], [25, 100], [15, 100], [27, 100], [18, 100], [28, 100], [18, 100], [30, 100], [18, 100], [28, 100], [23, 100], [28, 100], [23, 100], [28, 100], [23, 100], [28, 100], [23, 100], [28, 100], [28, 100], [28, 100], [28, 100], [23, 100], [28, 100], [23, 100], [27, 100], [22, 100], [30, 100], [27, 100], [30, 100], [22, 100], [29, 100], [27, 100], [29, 100], [22, 100], [27, 100], [22, 100], [30, 100], [27, 100], [23, 100], [30, 100], [27, 100], [23, 100], [30, 100], [27, 100], [23, 100], [27, 100], [23, 100], [20, 100], [27, 100], [23, 100], [27, 100], [20, 100], [28, 100], [23, 100], [28, 100], [20, 100], [30, 100], [23, 100], [30, 100], [20, 100], [28, 100], [23, 100], [28, 100], [23, 100], [28, 100], [23, 100], [30, 100], [27, 100], [22, 100], [30, 100], [27, 100], [22, 100], [28, 100], [22, 100], [27, 100], [22, 100], [27, 100], [19, 100], [31, 100], [27, 100], [31, 100], [22, 100], [27, 100], [22, 100], [27, 100], [22, 100], [27, 100], [19, 100], [27, 100], [27, 100], [30, 100], [27, 100], [30, 100], [27, 100], [27, 100], [27, 100], [23, 100], [23, 100], [28, 100], [30, 100], [30, 100], [30, 100], [30, 100], [30, 100], [30, 100], [30, 100], [23, 100], [15, 100], [20, 100], [22, 100], [23, 100], [28, 100], [22, 100], [23, 100], [20, 100], [25, 100], [30, 100], [22, 100], [25, 100], [28, 100], [27, 100], [28, 100], [27, 100], [22, 100], [23, 100], [27, 100], [22, 100], [20, 100], [22, 100], [23, 100], [27, 100], [22, 100], [23, 100], [27, 100], [22, 100], [25, 100], [25, 100], [30, 100], [27, 100], [30, 100], [25, 100], [10, 100], [25, 100], [17, 100], [22, 100], [24, 100], [30, 100], [25, 100], [24, 100], [25, 100], [22, 100], [27, 100], [25, 100], [27, 100], [30, 100], [29, 100], [30, 100], [29, 100], [27, 100], [29, 100], [25, 100], [24, 100], [22, 100], [24, 100], [25, 100], [30, 100], [27, 100], [29, 100], [30, 100], [20, 100], [8, 100], [12, 100], [15, 100], [18, 100], [20, 100], [15, 100], [12, 100], [6, 100], [8, 100], [12, 100], [15, 100], [18, 100], [20, 100], [15, 100], [12, 100], [6, 100], [24, 100], [8, 100], [12, 100], [15, 100], [18, 100], [20, 100], [15, 100], [12, 100], [6, 100], [8, 100], [12, 100], [15, 100], [18, 100], [20, 100], [15, 100], [24, 100], [18, 100], [8, 100], [27, 100], [12, 100], [15, 100], [18, 100], [20, 100], [15, 100], [12, 100], [6, 100], [8, 100], [12, 100], [15, 100], [18, 100], [20, 100], [15, 100], [12, 100], [6, 100], [8, 100], [30, 100], [12, 100], [15, 100], [18, 100], [20, 100], [15, 100], [12, 100], [6, 100], [8, 100], [12, 100], [15, 100], [18, 100], [20, 100], [24, 100], [27, 100], [30, 100], [20, 100], [24, 100], [27, 100], [30, 100], [27, 100], [24, 100], [18, 100], [20, 100], [24, 100], [27, 100], [30, 100], [27, 100], [24, 100], [18, 100], [20, 100], [24, 100], [27, 100], [30, 100], [27, 100], [24, 100], [18, 100], [20, 100], [24, 100], [27, 100], [30, 100], [27, 100], [30, 100], [20, 100], [24, 100], [27, 100], [30, 100], [27, 100], [24, 100], [18, 100], [20, 100], [24, 100], [27, 100], [30, 100], [27, 100], [24, 100], [18, 100], [20, 100], [24, 100], [27, 100], [30, 100], [27, 100], [24, 100], [18, 100], [20, 100], [24, 100], [27, 100], [30, 100], [27, 100], [27, 100], [24, 100], [20, 100], [29, 100], [25, 100], [29, 100], [25, 100], [29, 100], [25, 100], [30, 100], [25, 100], [20, 100], [30, 100], [25, 100], [30, 100], [20, 100], [29, 100], [25, 100], [20, 100], [29, 100], [25, 100], [20, 100], [29, 100], [25, 100], [20, 100], [27, 100], [24, 100], [20, 100], [15, 100], [27, 100], [24, 100], [20, 100], [27, 100], [24, 100], [15, 100], [27, 100], [29, 100], [22, 100], [17, 100], [29, 100], [27, 100], [22, 100], [17, 100], [29, 100], [27, 100], [22, 100], [17, 100], [25, 100], [29, 100], [22, 100], [17, 100], [25, 100], [29, 100], [22, 100], [25, 100], [29, 100], [17, 100], [24, 100], [29, 100], [22, 100], [17, 100], [29, 100], [24, 100], [22, 100], [17, 100], [29, 100], [25, 100], [17, 100], [29, 100], [27, 100], [17, 100], [29, 100], [20, 100], [30, 100], [20, 100], [20, 100], [30, 100], [25, 100], [30, 100], [25, 100], [30, 100], [25, 100], [30, 100], [25, 100], [30, 100], [30, 100], [30, 100], [30, 100], [25, 100], [30, 100], [25, 100], [29, 100], [24, 100], [29, 100], [24, 100]]]
function preload() {
for (let i = 1; i <= 10; i++) {
sounds.set(`multiplier${i}`, loadSound(`sounds/multiplier${i}.mp3`));
}
print('hi')
console.log('preloading')
for (let i = 1; i <= 5; i++) {
sounds.set(`ball${i}`, loadSound(`sounds/ball${i}.mp3`));
}
sounds.set("maxmultiplier", loadSound("sounds/maxmultiplier.mp3"));
for (let i = -MIDDLE_C_INDEX; i <= NUM_PITCHES - MIDDLE_C_INDEX; i++) {
if (i !== 0) { // Skipping pitch_0.mp3 if it's not needed
soundFiles.bell[i + MIDDLE_C_INDEX] = loadSound(`instrument/bell/pitch_${i}.mp3`);
}
}
// melodyData = loadJSON('melody.json');
console.log('preloaded')
}
var pegCanvas;
let currentSongIndex = 0; // Index to select which song to play
let currentNoteIndex = 0; // Index to select which note to play in the current song
let lastNotePlayed = 0; // Time when the last note was played
function playSong() {
let currentSong = songs[currentSongIndex];
let currentNotePair = currentSong[currentNoteIndex];
// if (!currentNotePair) {
// print('returned')
// print(currentNotePair)
// noLoop()
// return
// }
// print(currentNotePair)
let currentNoteDuration = currentNotePair[1];
if (millis() - lastNotePlayed > currentNoteDuration) {
playNote(currentNotePair[0] % NUM_PITCHES) ;
currentNoteIndex = (currentNoteIndex + 1) % currentSong.length;
lastNotePlayed = millis();
if (currentNoteIndex === 0) {
print('next!')
currentSongIndex = (currentSongIndex + 1) % songs.length;
}
}
}
var alwaysPlaySong;
// Function to play a note relative to middle C
function playNote(relativeIndex) {
// let soundIndex = MIDDLE_C_INDEX + relativeIndex;
if (soundFiles.bell[relativeIndex]) {
soundFiles.bell[relativeIndex].play();
}
}
function getSound(type, index) {
if (type === "ball" || type === "multiplier") {
if (index >= 0 && index <= 5) {
return sounds.get(`${type}${index}`);
}
} else if (type === "maxmultiplier" && index === 6) {
return sounds.get("maxmultiplier");
}
return null;
}
function setup() {
createCanvas(900, 900);
pegCanvas = createGraphics(900, 900);
// songs.push(melodyData)
print(soundFiles.bell.length, 'bell sounds loaded')
// [1,]
randomSeed("blep");
BUCKET_WIDTH = width / NUM_BUCKETS;
debug = createCheckbox("debug");
engine = Engine.create();
world = engine.world;
setupWalls();
createBuckets();
createPegs();
let button = createButton("Buy Ball");
button.mousePressed(spawnBall);
button.position(30, 40);
exportButton = createButton("Export to CSV");
exportButton.position(width / 2, height + 20); // Adjust position as needed
exportButton.mousePressed(exportToCSV);
exportBallHistoryBtn = createButton(`exportBallHistoryToCSV`);
exportBallHistoryBtn.position(width / 2, height + 50);
exportBallHistoryBtn.mousePressed(exportBallHistoryToCSV);
autobuy = createCheckbox("auto-buy").checked(false);
alwaysPlaySong = createCheckbox("play song always")
volumeSlider = createSlider(0, 1, 0.5, 0.05);
volumeSlider.input(adaptVolume);
handleCollisions();
adaptVolume()
}
function adaptVolume() {
for (const [k, value] of Object.entries(sounds)) {
value.setVolume(volumeSlider.value());
}
for (let i = -MIDDLE_C_INDEX; i <= NUM_PITCHES - MIDDLE_C_INDEX; i++) {
if (i !== 0) { // Skipping pitch_0.mp3 if it's not needed
soundFiles.bell[i + MIDDLE_C_INDEX].setVolume(volumeSlider.value())
}
}
}
function exportBallHistoryToCSV() {
// CSV Headers
let csvContent = "mult,x,y,bounce_amt\n";
// Convert each list of integers to a CSV row
LogBalls.forEach((row) => {
csvContent += row.join(",") + "\n";
});
// Create a blob and link to download the CSV
const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = "ballHistoryMultipleirs.csv";
link.style.visibility = "hidden";
// Append to the document and trigger download
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function exportToCSV() {
let csvContent = "data:text/csv;charset=utf-8,";
// Add header
csvContent += "Bucket Index, Balls Captured, Multiplier\n";
// Add data
scores.forEach((score, index) => {
let multiplier = getValueForBucket(index);
csvContent += index + "," + score + "," + multiplier + "\n";
});
// Create a link and download
var encodedUri = encodeURI(csvContent);
var link = document.createElement("a");
link.setAttribute("href", encodedUri);
link.setAttribute("download", "bucket_data.csv");
document.body.appendChild(link); // Required for Firefox
link.click(); // Trigger download
}
let blink = true;
setInterval(() => {
blink = !blink;
}, 500); // Blink every 500 milliseconds
function draw() {
background(100, 0, 200);
if (alwaysPlaySong.checked()) playSong()
if (money < ballcost && blink) {
push();
textSize(100);
textAlign(CENTER);
fill("orange");
text("OUT OF MONEY", width / 2 + 4, height / 2 + 4);
fill(255, 0, 0);
text("OUT OF MONEY", width / 2, height / 2);
pop();
}
Engine.update(engine);
if (autobuy.checked() && balls.length < MAX_BALLS && blink) {
spawnBall();
}
balls.forEach((ball) => {
let velocity = ball.velocity;
// Clamp x-velocity
if (Math.abs(velocity.x) > MAX_X_VELOCITY) {
let clampedVelocityX = Math.sign(velocity.x) * MAX_X_VELOCITY;
Body.setVelocity(ball, { x: clampedVelocityX, y: velocity.y });
}
});
drawBuckets();
drawBalls();
removeOutOfBoundsBalls();
drawPegs();
displayScores();
nudgeStagnantBalls();
// text(`each 🔴 is one mcdonalds employee's monthly salary`, 20, 30);
text(`wallet $${money.toFixed(2)}`, 30, 80);
text(`profit $${profit.toFixed(1)}`, 30, 100);
text(`balls bought ${balls_spawned}`, 30, 120);
text(`$/ball ${profit_per_ball.toFixed(1)}`, 30, 140);
drawLogBook();
}
function drawLogBook() {
const xPosition = width - 150; // Adjust the x-position for the log entries
const yPosition = 50;
// Display log entries on the right side of the canvas
if (logEntries.length == 0) return;
push();
text("winnings", xPosition, yPosition);
textSize(16);
fill(0);
textAlign(LEFT, TOP);
for (let i = 0; i < logEntries.length; i++) {
text(logEntries[i], xPosition, yPosition + 20 * i);
}
pop();
// Remove the first entry if there are more than 50 entries
while (logEntries.length > 30) {
logEntries.shift();
}
}
function nudgeStagnantBalls() {
return; // this doesnt need to be done anymore
for (let ball of balls) {
// Check if the ball has moved in the last MOVE_THRESHOLD milliseconds
if (millis() - ball.lastMove > MOVE_THRESHOLD && !ball.isMoving) {
// Apply a small nudge force in a random direction
print("NUDGED");
Body.applyForce(ball, ball.position, {
x: (Math.random() - 0.5) * NUDGE_FORCE,
y: (Math.random() - 0.5) * NUDGE_FORCE,
});
ball.lastMove = millis(); // Update the last move time
}
// Update the isMoving flag based on the velocity
if (
Math.abs(ball.velocity.x) > 0.001 ||
Math.abs(ball.velocity.y) > 0.001
) {
ball.isMoving = true;
ball.lastMove = millis(); // Update the last move time if the ball is moving
} else {
ball.isMoving = false;
}
}
}
function createPegs() {
let yOffset = TOP_MARGIN;
let rowSpacing = (height - TOP_MARGIN - BOTTOM_MARGIN) / (PEG_ROWS + 1); // Add 1 to create space at the bottom
for (let row = 0; row < PEG_ROWS; row++) {
if (row < 2) continue;
let numPegsInRow = row + 1;
let xOffset = (width - PEG_SPACING * (numPegsInRow - 1)) / 2; // Center the pyramid
for (let col = 0; col < numPegsInRow; col++) {
let x = xOffset + col * PEG_SPACING; // + random(-1,1);
let y = yOffset + row * rowSpacing; //+ random(-1,1);;
let peg = new Peg(x, y, PEG_SIZE / 2);
pegs.push(peg);
World.add(world, peg.body);
}
}
}
function setupWalls() {
let wallOptions = {
isStatic: true,
restitution: 1.0, // This value can be between 0 and 1; adjust as needed for the desired effect
};
// Bottom wall
World.add(world, Bodies.rectangle(width / 2, height, width, 50, wallOptions));
// Left wall
World.add(world, Bodies.rectangle(0, height / 2, 5, height, wallOptions));
// Right wall
World.add(world, Bodies.rectangle(width, height / 2, 5, height, wallOptions));
}
function createBuckets() {
let bucketSpacing = width / NUM_BUCKETS;
for (let i = 0; i < NUM_BUCKETS; i++) {
let x = i * bucketSpacing + bucketSpacing / 2;
let bucket = Bodies.rectangle(
x,
height - BUCKET_HEIGHT / 2,
bucketSpacing,
BUCKET_HEIGHT,
{ isStatic: true }
);
buckets.push(bucket);
World.add(world, bucket);
}
}
function drawPegs() {
for (let peg of pegs) {
peg.update();
peg.show();
}
}
function spawnBall() {
if (ballcost > money) {
print("No money left!");
return;
} else {
money -= ballcost;
profit -= ballcost;
balls_spawned++;
profit_per_ball = profit / balls_spawned;
}
const lr = [width / 2 - 20, width / 2 + 20];
pos = { x: random(lr) + random(-15, 15), y: -15 };
let ball = Bodies.circle(pos.x, pos.y, BALL_RADIUS, {
restitution: 0.5, // Less bouncy
friction: 0.05,
frictionAir: 0.01, // Slight air resistance
density: 0.5, // Adjust density
collisionFilter: {
category: ballCategory,
mask: defaultCategory, // Balls will only collide with default category
},
});
// Matter.Body.setMass(ball, 2);
// Matter.Body.setInertia(ball, 1);
// for manipulation
ball.startpos = pos;
ball.pegbounce = 0;
ball.lastMove = millis();
ball.cost = ballcost; // and we're assuming the user hasnt changed it that fast xd
ball.isMoving = true;
balls.push(ball);
World.add(world, ball);
// Apply a small sideways force to the ball
// The direction is random: either to the left or to the right
// let forceMagnitude = 0.0002; // Adjust this value as needed
// let forceDirection = Math.random() < 0.5 ? -1 : 1;
// Body.applyForce(ball, ball.position, {
// x: forceMagnitude * forceDirection,
// y: 0,
// });
}
function drawBuckets() {
const middle = color("yellow");
const outer = color("red");
const centerBucketIndex = (NUM_BUCKETS - 1) / 2;
const maxColorDistance = Math.max(
centerBucketIndex,
NUM_BUCKETS - centerBucketIndex - 1
);
for (let i = 0; i < NUM_BUCKETS; i++) {
// Determine the relative distance from the center bucket for the color gradient
const distanceFromCenter = Math.abs(i - centerBucketIndex);
fill(lerpColor(middle, outer, distanceFromCenter / centerBucketIndex));
let bucket = buckets[i];
rect(
bucket.position.x - BUCKET_WIDTH / 2,
height - BUCKET_HEIGHT,
BUCKET_WIDTH,
BUCKET_HEIGHT
);
}
}
function drawBalls() {
for (let ball of balls) {
fill(255, 0, 0);
circle(ball.position.x, ball.position.y, BALL_RADIUS * 2);
}
}
function removeOutOfBoundsBalls() {
balls = balls.filter((ball) => {
if (ball.position.y > height) {
World.remove(world, ball);
return false;
}
return true;
});
}
function handleCollisions() {
Events.on(engine, "collisionStart", function (event) {
event.pairs.forEach(function (pair) {
const { bodyA, bodyB } = pair;
// Check if the pair includes a ball and either a peg or a bucket
let ball = balls.includes(bodyA)
? bodyA
: balls.includes(bodyB)
? bodyB
: null;
let peg = pegs.find((p) => p.body === bodyA || p.body === bodyB);
let bucket = buckets.includes(bodyA)
? bodyA
: buckets.includes(bodyB)
? bodyB
: null;
// Apply force towards center and spin if a ball collides with a peg
if (ball && peg) {
peg.lightUp(); // Light up the peg
ball.pegbounce++;
const chance = abs(ball.position.x) / width / 2;
if (random() < chance) {
Body.setVelocity(ball, {
x: -ball.velocity.x,
y: ball.velocity.y,
});
}
playSong()
// Logic for applying force towards the center
// let centerX = width / 2;
// let distanceFromCenter = Math.abs(peg.body.position.x - centerX);
// let forceMagnitude = map(distanceFromCenter, 0, width / 2, 0, 1); // Adjust max force as needed
// let forceDirection = peg.body.position.x > centerX ? -1 : 1;
// print(forceMagnitude, forceDirection)
// print(Body.getVelocity(ball))
// Body.applyForce(ball, ball.position, {
// x: forceMagnitude * forceDirection,
// y: -1
// });
// print(ball.velocity)
// // Logic for applying spin
// let spinMagnitude = 0.0001; // Adjust the spin magnitude as needed
// let spinDirection = Math.random() < 0.5 ? -1 : 1; // Random direction
// Body.setAngularVelocity(ball, spinMagnitude * spinDirection);
}
// Logic for handling ball-bucket collisions
if (ball && bucket) {
// Find bucket index by matching the id
const bucketIndex = buckets.indexOf(bucket);
if (bucketIndex !== -1) {
// Increment the score for the bucket
if (bucketIndex == 0 || bucketIndex == buckets.length - 1) {
sounds.get("maxmultiplier").play();
} else {
const distanceFromCenter = Math.abs(
bucketIndex - centerBucketIndex
);
try {
// sounds.get(`multiplier${distanceFromCenter + 1}`).play();
} catch {
print(`multiplier${distanceFromCenter + 1}`);
}
}
scores[bucketIndex]++;
multiplier = getValueForBucket(bucketIndex);
ball.multiplier = multiplier;
money += ballcost * multiplier;
profit += ballcost * multiplier;
logEntries.push(`${ballcost * multiplier} (${multiplier})`);
LogBalls.push([
multiplier,
ball.startpos.x,
ball.startpos.y,
ball.pegbounce,
]);
profit_per_ball = profit / balls_spawned;
// Remove the ball
World.remove(world, ball);
balls = balls.filter((b) => b !== ball); // Remove the ball from the balls array
}
}
});
});
}
function getValueForBucket(bucketIndex) {
const distanceFromCenter = Math.abs(bucketIndex - centerBucketIndex);
return BUCKET_INCREMENTS[distanceFromCenter];
}
function displayScores() {
push();
textAlign(CENTER);
for (let i = 0; i < NUM_BUCKETS; i++) {
fill(0);
textSize(16);
text(scores[i], i * BUCKET_WIDTH + BUCKET_WIDTH / 2, height - 20);
text(
getValueForBucket(i).toFixed(2),
i * BUCKET_WIDTH + BUCKET_WIDTH / 2,
height - 45
);
}
pop();
}
class Peg {
constructor(x, y, radius) {
this.body = Bodies.circle(x, y, PEG_SIZE / 2, {
isStatic: true,
collisionFilter: {
category: defaultCategory,
},
});
this.radius = radius;
this.isLit = false;
this.litIntensity = 0;
this.LIT_DURATION = 1000; // Duration for the peg to stay lit
}
lightUp() {
if (!this.isLit) {
var soundNum = int((this.body.position.x + this.body.position.y) % 4);
// sounds.get(`ball${soundNum + 1}`).play();
}
this.isLit = true;
this.litTime = millis();
}
update() {
if (this.isLit) {
let elapsed = millis() - this.litTime;
if (elapsed < this.LIT_DURATION) {
this.litIntensity = 1 - elapsed / this.LIT_DURATION; // Decreases from 1 to 0
} else {
this.isLit = false;
}
}
}
show() {
let pos = this.body.position;
let basePegColor = color("orange"); // Color of the peg itself
let lightColor = color(255, 255, 0, 100); // Yellow light with some transparency
let lightRadius = this.radius * 2 + 20; // Radius for the light effect
push();
// Always draw the peg
fill(basePegColor);
circle(pos.x, pos.y, this.radius * 2);
if (this.isLit) {
noStroke();
// Calculate current radius for the light effect based on lit intensity
let currentLightRadius = lightRadius * this.litIntensity;
// Set alpha for the light effect
lightColor.setAlpha(100 * this.litIntensity); // Adjust transparency
// Draw the light effect around the peg
fill(lightColor);
circle(pos.x, pos.y, currentLightRadius);
}
pop();
}
}