xxxxxxxxxx
740
/*
FULLSCREEN LINK : https://editor.p5js.org/Queuebee2/full/K7OQgJhAW
changelog
19-03-2024
- added some more achievements and eastereggs
18-03-2024
- added buggy achievement panel
- added some achievements
17-03-2024
- added some easter eggu
14-02-2024
- started adding css
- added autoclicker visualisation
- added stats overlay with
- 'mode' graph
- 'mathematical average'
- made autoclicker click faster but lag your pc more
-
TODO :
dont acutally sim autoclicker, its awful. just use the known ratio
to predict 100 new click values in thehistogram and add at the corresponding amount of clicks
*/
const BRONZE = '#CD7F32'
const SILVER = '#C0C0C0'
const GOLD = '#FFD700'
let mvec;
let circlemiddle;
const circleRadius = 100;
let clicks_total = 0;
let clicks_total_manual = 0;
let clicks_this_manual = 0;
let clicks_this = 0;
let best = 0;
let click_gray = 0;
let autoclick_chk;
let polysynth;
let soundQueue = [];
const MAX_SOUNDS = 7; // Maximum simultaneous sounds
let currentSounds = 0;
let enable_sound_chk;
let click_counts = []
let maxval = 0.01 //prevents a zerodiv error :^)
let draw_stats_chk
let maxvalnum = 0; // this is the number that has the maximum amount of.,.. clicks..had.. statistics.. idk.
let notes_played = 0
let records_summed_up = 0
let total_resets = 0
let marijke_chk;
let achievementsCache = {};
let saveTimeout;
function saveAchievementsDebounced() {
clearTimeout(saveTimeout);
saveTimeout = setTimeout(() => {
localStorage.setItem('achievements', JSON.stringify(achievementsCache));
}, 1000); // Wait for 1 second of inactivity before saving
}
// Function to load achievements from localStorage into memory
function loadAchievements() {
const storedAchievements = localStorage.getItem('achievements');
achievementsCache = storedAchievements ? JSON.parse(storedAchievements) : {};
updateAchievementsUI();
// After UI is updated, adjust the panel based on achievements presence
if (Object.keys(achievementsCache).length > 0) {
ensurePanelState(true); // True if you want the panel to be expanded when achievements are present
} else {
ensurePanelState(false); // False to keep it collapsed or hidden when empty
}
}
// Modified addAchievement function to use the in-memory cache
function addAchievement(id, explanation, backgroundColor = '#4CAF50', textColor = 'white') {
if (Object.keys(achievementsCache).length === 1) { // If this is the first achievement added
ensurePanelState(true); // Make the panel visible and expanded
}
let achievementText = id ;
if (achievementsCache[id]) {
// TODO : MAKE IT WAY MORE EFFICIENT THAN THIS CRAP
// nobody cares :^)
// console.log("Achievement already exists.");
return; // Prevent adding the achievement if it already exists
}
const timestamp = new Date().toLocaleString(); // Get current time and date
achievementsCache[id] = { achievementText, explanation, backgroundColor, textColor, timestamp };
// Optionally, save back to localStorage less frequently, or upon specific triggers
localStorage.setItem('achievements', JSON.stringify(achievementsCache));
// Add achievement to the UI
updateAchievementsUI();
}
function updateAchievementsUI() {
const achievementDiv = document.getElementById('achievementDiv');
// Find or create the achievementsHeader
let achievementsHeader = document.getElementById('achievementsHeader');
if (!achievementsHeader) {
achievementsHeader = document.createElement('div');
achievementsHeader.id = 'achievementsHeader';
achievementDiv.insertBefore(achievementsHeader, achievementDiv.firstChild);
}
// Clear existing achievements UI but keep the header
while (achievementDiv.children.length > 1) {
achievementDiv.removeChild(achievementDiv.lastChild);
}
Object.entries(achievementsCache).forEach(([id, { achievementText, explanation, backgroundColor, textColor, timestamp }]) => {
const achievement = document.createElement('p');
achievement.textContent = `${achievementText} - ${timestamp}`;
achievement.className = 'achievement-item';
achievement.style.backgroundColor = backgroundColor;
achievement.style.color = textColor;
achievement.title = explanation;
achievementDiv.appendChild(achievement);
});
// Re-apply the correct header state based on panel expansion
ensurePanelState(true);
}
function saveAchievement(id, details) {
achievementsCache[id] = details;
localStorage.setItem('achievements', JSON.stringify(achievementsCache));
updateAchievementsUI();
}
function ensurePanelState(shouldExpand) {
const achievementDiv = document.getElementById('achievementDiv');
const achievementsHeader = document.getElementById('achievementsHeader');
// Handle visibility based on whether there are achievements to show
if (Object.keys(achievementsCache).length === 0) {
// If there are no achievements, hide the panel completely
achievementDiv.classList.add('hidden');
} else {
// Remove 'hidden' if it was added
achievementDiv.classList.remove('hidden');
if (shouldExpand) {
achievementDiv.classList.add('achievementDiv-expanded');
achievementDiv.classList.remove('achievementDiv-collapsed');
achievementsHeader.classList.add('horizontal-header');
achievementsHeader.classList.remove('vertical-header');
achievementsHeader.textContent = 'Achievements';
achievementDiv.style.maxWidth = '200px';
} else {
achievementDiv.classList.remove('achievementDiv-expanded');
achievementDiv.classList.add('achievementDiv-collapsed');
achievementsHeader.classList.remove('horizontal-header');
achievementsHeader.classList.add('vertical-header');
achievementsHeader.textContent = 'A'; // or keep 'Achievements' if it fits or looks better
achievementDiv.style.maxWidth = '20px';
}
}
// Adjust height only if expanding and there are achievements
adjustAchievementsHeight(shouldExpand && Object.keys(achievementsCache).length > 0);
}
function adjustAchievementsPanel() {
const achievementDiv = document.getElementById('achievementDiv');
const achievements = JSON.parse(localStorage.getItem('achievements')) || {};
if (Object.keys(achievements).length > 0) {
// Ensure the panel is correctly sized and shown
achievementDiv.classList.add('achievementDiv-expanded');
achievementDiv.style.maxWidth = '200px';
adjustAchievementsHeight();
} else {
// Hide or collapse the panel if there are no achievements
achievementDiv.classList.remove('achievementDiv-expanded');
achievementDiv.style.maxWidth = '20px'; // Or other logic for hiding/collapsing
}
}
function adjustAchievementsHeight() {
const canvasDiv = document.getElementById('canvasDiv');
const controlsDiv = document.getElementById('controlsDiv');
const achievementDiv = document.getElementById('achievementDiv');
const canvasHeight = canvasDiv.offsetHeight;
const controlsHeight = controlsDiv.offsetHeight;
// Update both height and max-height
const totalHeight = `${(canvasHeight + controlsHeight) * .8}px`;
achievementDiv.style.height = totalHeight;
achievementDiv.style.maxHeight = totalHeight;
}
function clearAchievements() {
print('removing achievements please reload the page')
localStorage.removeItem('achievements');
achievementsCache = {}
updateAchievementsUI();
toggleAchievements();
}
// Optional: Function to toggle the achievements panel, refactored to use updateAchievementsUI
function toggleAchievements() {
const achievementDiv = document.getElementById('achievementDiv');
const achievementsHeader = document.getElementById('achievementsHeader');
if (achievementDiv.classList.contains('achievementDiv-expanded')) {
achievementDiv.classList.remove('achievementDiv-expanded');
achievementDiv.classList.add('achievementDiv-collapsed');
achievementDiv.style.maxWidth = '20px'; // Collapse to a thin vertical bar
// Change header to vertical
achievementsHeader.classList.remove('horizontal-header');
achievementsHeader.classList.add('vertical-header');
achievementsHeader.textContent = 'Achievements'; // Ensure correct spelling for vertical layout
// Hide achievements content when collapsed
Array.from(achievementDiv.children).forEach(child => {
if (child.id !== 'achievementsHeader') {
child.style.display = 'none';
}
});
} else {
achievementDiv.classList.add('achievementDiv-expanded');
achievementDiv.classList.remove('achievementDiv-collapsed');
achievementDiv.style.maxWidth = '200px'; // Expand
// Change header to horizontal
achievementsHeader.classList.add('horizontal-header');
achievementsHeader.classList.remove('vertical-header');
achievementsHeader.textContent = 'Achievements';
// Show achievements content
Array.from(achievementDiv.children).forEach(child => {
child.style.display = ''; // Reset display for new items
});
}
}
document.getElementById('achievementDiv').addEventListener('click', toggleAchievements);
function setup() {
let canvas = createCanvas(400, 400);
frameRate(60);
// enable when debugging :)
// localStorage.clear()
canvas.parent('canvasDiv')
loadAchievements()
// addAchievement('Test Achievement', 'this is a test. achievments will come soon!', 'red', 'green')
toggleAchievements() // close the panel
textAlign(CENTER);
mvec = createVector();
autoDiv = createDiv().parent('controlsDiv')
marijke_chk = createCheckbox('Heet je Marijke?').hide().input(function(){
addAchievement('Marijke', 'Je heet Marijke', 'yellow', 'rgb(0,255,255)')
this.input = null
})
autoclick_chk = createCheckbox("Autoclicker?").parent('controlsDiv').input(
function(e){
if (this.checked()) {
marijke_chk.show()
addAchievement('Cheater', "Not everyone has time to waste their time on silly games",
'black', 'green')
} else {
marijke_chk.hide()
}
}).parent(autoDiv)
marijke_chk.parent(autoDiv)
enable_sound_chk = createCheckbox('Sound').checked(true).parent('controlsDiv').input(
function(){
addAchievement('MAKE IT STOP!!!!', "Beep boop boop beep skibidi fortnite beep boop",
'rgb(62,0,103)','rgb(255,0,0)' )
}
);
draw_stats_chk = createCheckbox('Draw stats overlay').checked(true).parent('controlsDiv')
createButton('Download Stats').mousePressed(function(e){
saveJSON({
total_clicks: clicks_total,
clicks_last_run : clicks_this,
record: best,
count_per_click_record: click_counts,
}, 'saved.json')
}).parent('controlsDiv')
createButton('Reset Achievements').mousePressed(function(e){
clearAchievements()
}).parent('controlsDiv')
circlemiddle = createVector(height / 2, width / 2);
polysynth = new p5.PolySynth();
for (var i = 0 ; i < 101; i++){
click_counts.push(0)
}
}
let lastkeys = [];
const N = 6; //
const wordToCheck = "freek";
function checkWordSpelled(word) {
const keyString = lastkeys.join('');
return keyString.includes(word.toLowerCase());
}
function keyPressed() {
lastkeys.push(key.toLowerCase()); // Assuming case-insensitivity
if (lastkeys.length > N) {
lastkeys.shift(); // Keep only the last N key presses
}
}
function draw() {
clear();
dumbAchievementSystem('framecount')
push()
fill("rgb(57,57,57)");
circle(width / 2, height / 2, 2 * circleRadius + 10);
fill("rgba(255,0,0,0.5)");
circle(width / 2, height / 2, 2 * circleRadius);
pop()
mvec.x = mouseX;
mvec.y = mouseY;
circle(mvec.x, mvec.y, 10);
// circle(circlemiddle.x, circlemiddle.y, 5)
// console.debug(mouseInCircle())
push()
fill("blue");
text(`Total clicks ${clicks_total}`, width / 2, 20);
text(`Clicks this run: ${clicks_this}`, width / 2, 36);
text(`Record: ${best}`, width / 2, 49);
// text(`best: ${best}`, width/2 , 58)
pop()
autoClick()
push()
textAlign(CENTER)
fill('black')
textSize(40)
text(clicks_this, circlemiddle.x, circlemiddle.y+10);
pop();
// Check if we can play a sound
if (soundQueue.length > 0 && currentSounds < MAX_SOUNDS) {
let soundData = soundQueue.shift(); // Remove the first item from the queue
playSound(soundData); // Play the sound
}
if (draw_stats_chk.checked()){
draw_statsOverlay()
}
// Example usage: Check if the word was spelled in the last N key presses
if (checkWordSpelled(wordToCheck)) {
push()
textSize(70);
fill(GOLD)
text("FREEK IS\nEEN FREEK", 200, 200);
addAchievement('Freek', 'Freek is een freek is een freek is een freek is een freek is een freek!', GOLD, 'rgb(255,0,0)')
pop()
}
}
function autoClick() {
if(marijke_chk.checked() && !mouseIsPressed){
push()
textAlign(CENTER)
textWrap(WORD)
rectMode(CENTER) //rectmode works on text if a rect is given for the area.. or something.
// hover over the center is not really what i expected but w/e
text('De Marijke-mode-autoclicker werkt alleen\n als je de knop ingedrukt houdt',
width/2,height/2,
100, 100)
pop()
return
}
if (autoclick_chk.checked()){
if (frameCount % (30 + int(noise(frameCount/100)*5)) == 0) {
triggerSound()
}
push()
textSize(90 + noise(frameCount/10000)*20)
switch ( int(random()*5)) {
case 0:
case 1:
text('👇', 200+ random()*5, 150+ random()*5);
break
case 2:
text('👈', 270+ random()*5, 220+ random()*5);
break
case 3:
text('👉', 130+ random()*5, 220+ random()*5);
break
case 4:
text('👆', 200+ random()*5, 300+ random()*5);
break
case 5:
text('🖖', 200+ random()*5, 200+ random()*5);
break
default:
text('👇👈👉👆🖖', 50, 50);
break;
}
text('🤖', 250+ random()*15, 150 + random()*15);
text('🤖', 150+ random()*15, 150 + random()*15);
pop()
}
if (autoclick_chk.checked()) {
for (var i = 0; i < 100000; i ++){
youClickedTheButton(true);
}
}
}
function mathematicalAverage() {
return records_summed_up / total_resets
}
function draw_statsOverlay(){
if (clicks_total < 1) {return;}
for (var i =0 ; i < 101 ; i ++ ){
const val = click_counts[i]
const normalized_val = map(val, 0, maxval, 0, 1)
if (val >= 1)
{
const GREEN = color('green')
const BLUE = color('blue')
fill(lerpColor(BLUE, GREEN, normalized_val))
rect(i*5, height-10, 5, -normalized_val * 255)
if (i*5 % 20 == 0){
text(i, i*5, height)
}
}
if (i == maxvalnum) {
push()
textAlign(LEFT)
fill('red')
strokeWeight(1);
textSize(15)
line(0, height - normalized_val*255 -12, i*5, height - normalized_val*255-12)
text(`MODE\n${i} : ${val}`, 10, height - normalized_val*255 -34)
const avg = mathematicalAverage()
text(`Mathematical avg:${avg}`, 225, height-50);
pop()
}
}
}
// Function to enqueue sound
function enqueueSound(soundData) {
soundQueue.push(soundData); // Add new sound data to the queue
}
function triggerSound() {
let soundData = {};
enqueueSound(soundData);
}
function playSound(soundData) {
if (!enable_sound_chk.checked()) return ;
// Increment the count of current sounds
currentSounds++;
// Assuming the sound duration is 0.1 seconds,
// decrement currentSounds after the sound has played
if (currentSounds < 25 ){
const notes = ["A4", "G3", "B4", "F4"];
const note = random(notes);
// Play a note with polysynth, for example
polysynth.play(note, 0.3, 0.1, 0.08); // Note, velocity, start time, duration
notes_played ++;
// lmfao
if (notes_played == 10 ){
addAchievement('musicmaster 1', '10 notes played', 'red', 'green')
}
else if (notes_played == 25 ){
addAchievement('musicmaster 2!', '25 notes played', 'red', 'green')
}
else if (notes_played == 50 ){
addAchievement('musicmaster 3!!', '50 notes played', 'red', 'green')
}
else if (notes_played == 100 ){
addAchievement('musicmaster 4!!!', '100 notes played', 'red', 'green')
}
else if (notes_played == 200 ){
addAchievement('musicmaster 5!!!!', '200 notes played', BRONZE, 'green')
}
else if (notes_played == 400 ){
addAchievement('musicmaster 6!!!!!', '400 notes played', SILVER, 'green')
}
else if (notes_played == 800 ){
addAchievement('musicmaster 7!!!!!!', '800 notes played', GOLD, 'green')
}
setTimeout(() => {
currentSounds--;
}, 100);
}
}
function mouseInCircle() {
return mvec.dist(circlemiddle) <= circleRadius;
}
function makeItLooksLikeTheButtonIsPressed() {
if (click_gray > 0) {
push()
fill("rgb(177,177,177, 10)");
circle(width / 2, height / 2, 2 * circleRadius);
click_gray--;
pop()
}
}
function youClickedTheButton(overload = false) {
const mouseIsInCircle = mouseInCircle();
if (!overload && mouseIsInCircle) {
triggerSound();
dumbAchievementSystem('manual_click')
clicks_total_manual ++;
}
if (overload || mouseIsInCircle) {
makeItLooksLikeTheButtonIsPressed();
if (!overload) {
click_gray = min(click_gray + 10, 20);
}
if (random() * 100 > clicks_this) {
clicks_this++;
if (!autoclick_chk.checked()){
clicks_this_manual++
}
} else {
best = max(clicks_this, best);
click_counts[clicks_this]+=1
const prev = maxval
maxval = max(maxval, click_counts[clicks_this])
if (maxval-prev>0){
maxvalnum = clicks_this;
}
records_summed_up += clicks_this
clicks_this = 0;
clicks_this_manual=0;
total_resets ++;
}
clicks_total++;
}
}
function dumbAchievementSystem(item) {
/*
mega todo:
make more efficient through
1) achievement id
2) not re-checking the same achievements
3) use setInterval or smth for time-based achievmeents
*/
switch(item) {
case ('framecount'):
// assuming 60fps, which, is often not the case. seems broken
if (frameCount>600 && frameCount < 660){addAchievement('Button visitor', 'Visited the button for a minute', BRONZE, 'black')}
if (frameCount>36000 && frameCount < 36060){addAchievement('Button enjoyer', 'Visited the button for 10 minutes', SILVER, 'black')}
if (frameCount>216000 && frameCount < 216060){addAchievement('Button addict', 'Visited the button for an hour', GOLD, 'black')}
break;
case ('manual_click'):
if (clicks_total>10){addAchievement('Button presser', 'Pressed the button 10 times', BRONZE, 'cyan')}
if (clicks_total>100){addAchievement('Button slapper', 'Slapped the button 100 times', SILVER, 'cyan')}
if (clicks_total>1000){addAchievement('Button smasher', 'Smashed the button a whopping 1000 times, manually!', GOLD, 'cyan')}
/*
print(*enumerate([(eval(" * ".join([str((100-j)/100) for j in range(i)])))for i in range(1,51)]),sep='\n')
(0, 1.0)
(1, 0.99)
(2, 0.9702)
(3, 0.9410939999999999)
(4, 0.9034502399999998)
(5, 0.8582777279999998)
(6, 0.8067810643199997)
(7, 0.7503063898175998)
(8, 0.6902818786321918)
(9, 0.6281565095552946)
(10, 0.5653408585997651)
(11, 0.503153364153791)
(12, 0.44277496045533604)
(13, 0.38521421559614233)
(14, 0.3312842254126824)
(15, 0.28159159160078)
(16, 0.2365369369446552)
(17, 0.1963256576640638)
(18, 0.16098703928453229)
(19, 0.13039950182047116)
(20, 0.10431960145637693)
(21, 0.08241248515053778)
(22, 0.06428173841741948)
(23, 0.049496938581413)
(24, 0.03761767332187388)
(25, 0.02821325499140541)
(26, 0.020877808693640004)
(27, 0.015240800346357202)
(28, 0.010973376249377186)
(29, 0.007791097137057801)
(30, 0.00545376799594046)
(31, 0.0037630999171989173)
(32, 0.002558907943695264)
(33, 0.001714468322275827)
(34, 0.0011315490927020458)
(35, 0.0007355069102563299)
(36, 0.00047072442256405113)
(37, 0.0002965563862153522)
(38, 0.00018386495945351837)
(39, 0.0001121576252666462)
(40, 6.729457515998772e-05)
(41, 3.9703799344392756e-05)
(42, 2.3028203619747796e-05)
(43, 1.3126076063256243e-05)
(44, 7.350602595423497e-06)
(45, 4.042831427482923e-06)
(46, 2.1831289708407786e-06)
(47, 1.1570583545456127e-06)
(48, 6.016703443637186e-07)
(49, 3.068518756254965e-07)
(50, 1.5342593781274825e-07)
*/
if (clicks_this_manual>5){addAchievement('picocombo', 'You pressed the button 5 times without it resetting. (85% chance!)', BRONZE, 'cyan')}
if (clicks_this_manual>10){addAchievement('microcombo', "You pressed the button 10 times without it resetting. (56.5% chance!)", SILVER, 'cyan')}
if (clicks_this_manual>20){addAchievement('centicombo', 'You pressed the button 20 times without it resetting. (10.4% chance!)', SILVER, 'cyan')}
if (clicks_this_manual>30){addAchievement('decacombo', 'You pressed the button 30 times without it resetting. (0.545% chance!)', GOLD, 'cyan')}
if (clicks_this_manual>40){addAchievement('kilocombo', 'You pressed the button 40 times without it resetting. (0.000672% chance!)', GOLD, 'cyan')}
if (clicks_this_manual>50){addAchievement('yottacombo', 'You pressed the button a whopping 50 times without it resetting. (0.00000153% chance!)', GOLD, 'cyan')}
break
default:
break;
}
}
function mousePressed() {
userStartAudio()
youClickedTheButton();
}