1/** 2 * Fish Food Jumpscare System 3 * Plays a FNAF jumpscare at milestone fish food counts. 4 */ 5(function () { 6 'use strict'; 7 8 if (window.FishFoodJumpscare || window.__fishFoodJumpscare) return; 9 10 var MILESTONES = [666, 5000]; 11 var STORAGE_KEY = 'pupwife_fishfood_jumpscare_done'; 12 var JUMPSCARE_IMAGE = 'https://gifdb.com/images/high/fnaf-jumpscare-gif-file-360kb-80yj5ynuvdla1brr.webp'; 13 var JUMPSCARE_SOUND = 'https://raw.githubusercontent.com/pupwife/pupwife.github.io/main/songs/fnafscream.mp3'; 14 var JUMPSCARE_DURATION_MS = 2500; 15 16 var active = false; 17 var debug = false; 18 19 function getCurrentCount() { 20 return window.__puppyFoodCounter ? window.__puppyFoodCounter.count : 0; 21 } 22 23 function getNextMilestone(count) { 24 for (var i = 0; i < MILESTONES.length; i++) { 25 if (count < MILESTONES[i] && !hasTriggered(MILESTONES[i])) { 26 return MILESTONES[i]; 27 } 28 } 29 return null; 30 } 31 32 function logDebug() { 33 if (!debug) return; 34 var count = getCurrentCount(); 35 var next = getNextMilestone(count); 36 console.log( 37 'ð Fish food count: ' + count + (next ? '/' + next : '') + 38 ' | Mode: ' + (debug ? 'DEBUG' : 'NORMAL') + 39 ' | Triggered: ' + JSON.stringify(getTriggeredMilestones()) 40 ); 41 } 42 43 function getTriggeredMilestones() { 44 try { 45 var raw = localStorage.getItem(STORAGE_KEY); 46 if (!raw) return {}; 47 var parsed = JSON.parse(raw); 48 return parsed && typeof parsed === 'object' ? parsed : {}; 49 } catch (e) { 50 return {}; 51 } 52 } 53 54 function hasTriggered(milestone) { 55 return !!getTriggeredMilestones()[String(milestone)]; 56 } 57 58 function markMilestoneDone(milestone) { 59 var done = getTriggeredMilestones(); 60 done[String(milestone)] = true; 61 try { 62 localStorage.setItem(STORAGE_KEY, JSON.stringify(done)); 63 } catch (e) {} 64 } 65 66 function injectShakeStyle() { 67 if (document.getElementById('fishfood-jumpscare-style')) return; 68 var style = document.createElement('style'); 69 style.id = 'fishfood-jumpscare-style'; 70 style.textContent = 71 '@keyframes fishfood-jumpscare-shake {' + 72 '0%, 100% { transform: translate(0, 0); }' + 73 '10% { transform: translate(-8px, 4px); }' + 74 '20% { transform: translate(8px, -4px); }' + 75 '30% { transform: translate(-6px, -6px); }' + 76 '40% { transform: translate(6px, 6px); }' + 77 '50% { transform: translate(-4px, 2px); }' + 78 '60% { transform: translate(4px, -2px); }' + 79 '70% { transform: translate(-2px, 4px); }' + 80 '80% { transform: translate(2px, -4px); }' + 81 '90% { transform: translate(-1px, 1px); }' + 82 '}' + 83 'html.fishfood-jumpscare-shake {' + 84 'animation: fishfood-jumpscare-shake 0.15s infinite;' + 85 '}'; 86 document.head.appendChild(style); 87 } 88 89 function playJumpscare(milestone, options) { 90 options = options || {}; 91 if (active) return; 92 active = true; 93 if (options.saveMilestone !== false && typeof milestone === 'number') { 94 markMilestoneDone(milestone); 95 } 96 injectShakeStyle(); 97 98 var overlay = document.createElement('div'); 99 overlay.id = 'jumpscare-overlay'; 100 overlay.style.cssText = 101 'position:fixed;' + 102 'top:0;' + 103 'left:0;' + 104 'width:100vw;' + 105 'height:100vh;' + 106 'background:black;' + 107 'z-index:999999;' + 108 'display:flex;' + 109 'align-items:center;' + 110 'justify-content:center;' + 111 'overflow:hidden;'; 112 113 var img = document.createElement('img'); 114 img.src = JUMPSCARE_IMAGE; 115 img.alt = ''; 116 img.setAttribute('aria-hidden', 'true'); 117 img.style.cssText = 'max-width:100%;max-height:100%;object-fit:contain;'; 118 119 overlay.appendChild(img); 120 document.body.appendChild(overlay); 121 document.documentElement.classList.add('fishfood-jumpscare-shake'); 122 123 try { 124 var audio = new Audio(JUMPSCARE_SOUND); 125 audio.volume = 1; 126 audio.play().catch(function () {}); 127 } catch (e) {} 128 129 setTimeout(function () { 130 overlay.remove(); 131 document.documentElement.classList.remove('fishfood-jumpscare-shake'); 132 active = false;
133 }, JUMPSCARE_DURATION_MS); 134 } 135 136 function checkMilestones(count) { 137 if (typeof count !== 'number' || !isFinite(count)) return; 138 139 for (var i = 0; i < MILESTONES.length; i++) { 140 var milestone = MILESTONES[i]; 141 if (count === milestone && !hasTriggered(milestone)) { 142 playJumpscare(milestone); 143 return; 144 } 145 } 146 } 147 148 function onCollected() { 149 if (!window.__puppyFoodCounter) return; 150 logDebug(); 151 checkMilestones(window.__puppyFoodCounter.count); 152 } 153 154 window.addEventListener('fishfood:collected', onCollected); 155 156 var api = { 157 milestones: MILESTONES.slice(), 158 check: checkMilestones, 159 test: function () { 160 playJumpscare(null, { saveMilestone: false }); 161 }, 162 enableDebug: function (enabled) { 163 debug = enabled !== false; 164 logDebug(); 165 return debug; 166 }, 167 status: function () { 168 var count = getCurrentCount(); 169 return { 170 count: count, 171 milestones: MILESTONES.slice(), 172 triggered: getTriggeredMilestones(), 173 nextMilestone: getNextMilestone(count), 174 debug: debug, 175 active: active 176 }; 177 } 178 }; 179 180 window.FishFoodJumpscare = api; 181 window.__fishFoodJumpscare = api; 182})();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.