DeepCo Croaker

Queue empty, async unarmed, Layer done, and bonus tooltip alerts with persistent settings, test buttons, and a live log.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

You will need to install an extension such as Tampermonkey to install this script.

Tendrás que instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Tendrás que instalar una extensión como Tampermonkey antes de poder instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Tendrás que instalar una extensión como Stylus antes de poder instalar este script.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

Para poder instalar esto tendrás que instalar primero una extensión de estilos de usuario.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==UserScript==
// @name         DeepCo Croaker
// @author       M3P / ChatGPT
// @namespace    local
// @license      MIT
// @version      2026.04.14
// @description  Queue empty, async unarmed, Layer done, and bonus tooltip alerts with persistent settings, test buttons, and a live log.
// @match        https://*.deepco.app/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=deepco.app
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// ==/UserScript==

(function () {
    "use strict";

    /* ----------------- CONFIG ----------------- */
    const DEBUG = false;
    const SETTINGS_KEY = "CROAKER_ALERTS_V2";
    const SOUND_DATA = "data:audio/mpeg;base64,//uQxAAAAAAAAAAAAAAAAAAAAAAAWGluZwAAAA8AAAAOAAAbtgAYGBgYGBgYKCgoKCgoKDY2NjY2NjZGRkZGRkZGWlpaWlpaWnJycnJycnKAgICAgICAmJiYmJiYmJisrKysrKysxMTExMTExNTU1NTU1NTo6Ojo6Ojo9vb29vb29v////////8AAABkTEFNRTMuMTAwBN0AAAAAAAAAABUgJAKNQQAB9AAAG7arR/cyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA//uwxAAAArABCSAAAAFamGH2mIAAQIBaAAAIE8p8PwQABzDH///////h+ytKRxyMNpAPCta0Jbb95vPEy8V7CAFxUXDALA/CMO1HxWhY9hEspHtJiU7m4qFib/36liAgFEe9fq23sf/uz9xS2bp7B6zpNSyKw0x/A4ff//4PlkFHKmq1u1pjZDUVQ9FKF4fR6LocZd1aKqAIHmJApubqIW8OYq6y8ucLKlA4BVFRwnNmiByXqxJZjSNJU5nUqRE6ZjixMlNJZDEOEKykqtSiba0fxiZU0et12Hvc0tSLIl6oJqdR+LyzLFxFyKhTrWPJ4NGTCVV/Yxrv/z923UlEszsbsPq9TZa0zGqKx/////zm86fKxSY9XPlcsU0ttTMc///////43asSzGnt0mFjOGXWjM9GXQjVuzM4f///467//+/7+H4Xd18+//54auZ14nLJQ3KTZ/+rVmaqrvHmTFoM+2w5tcFHsTlCJcuDKOIK5PGacpBRNQ1UOckUCOFbCyhxJIopEOIELmFbA34QSHNFFKQ5xWNkSZIqbGJdLqRwmiDECJ36knUTQrYLPAM4WUOcVkkUUVrbdSReLxsmiiilQWiYol0VqOUQpMEVKZdNZsXnNC6tjUuoJHNE6zPMSKkyTpOl0xRnS6XS6eSUlaqpJ7VOtbf60TGtFFGpJJFExNXB30WWR2zaC5OtkKDg8IxMQYrqNAIyVQVzXbHx0mgMUgWK4M8eWiS3ti+CMUiM4dVUmNENk/omo1W5gXPndv2+/wOiUAZAZfceanJ+8wxIc3YYovchTpGDhpQ93nb6qb9MzM3zp+kzM7M537eYlOX/+5DE+AAbCZkn+ZwQAq2uJP+fEASewSOGjSVSkWkVlQTQZGoCcDAIBJ6dh2QQMW7d9tLyYBmTZ+uTPWXigsRKT0QgbGTgMsV4AsIimZkBfN6U6zFBQkBDhfAyQzNruY1pCWwTljcUPNE2gHTdeTxCMySEL5cqCk4IAUVMMsBRntMDqLT2O5ev+yqLt2fZf7AMzikNghPgxwS+e//DVyDdbrYcyxA2YBbOeMBZnEgZQi7cv///99k1W/3DXN1i3igk47EOXnQYYwS5/4/vn//9k+GeGfc/3p74hGEeyy4cGZQCRDJG3hj///5r98/9//Pw5yrcztd//77qQ/bl8ypmscuIoI4jr0/2OIc5c0g1ggIQAFIAhA4EhCJAYaFM3GHGHGrcoexx570O0Rk4NYS2Z08JiRKLBCfSl8GTxc4LB5lzfi/Q8AUzCB4LOWlDADlpIECTJSMlCnOnTSigADsCSkCiRi4IWAswUWMLBJ5+4blMYbmupt0PpMAQKHjlFEICXZpfIQYDMzQss5CBgsxIiVnlEuQEqrIpPNGoBLmzs7T/+4DE/AAOCQcPtJYAA8KzI/81kkAzMzSt92ZoKzG2kwwFwZAND1+afqNPskNBFLbosMf/bLZdGqOi//4oaWtTFzZCmIy1wmvfKbuGf6oO5//4We/UjVPSd/3BvvCw36RG5DFHHPsphmNT3/+9Z/z//9/////20Bad4Z3Z2Zodnl5//lsrbSQAMohhummVAV1Uu5n4p0osGWNg9IKei0w/kMmFCAWAGqNfFgimYCmgpYu9uZ7K2ZGgEQMnsZECICZ1yTRzcvQhA3drLfPSwZ15DTw67SpopFHkrNcWsIAEwwdYA01+M6JTbHPckU7Yn3HLdWnpIxDLdsanbmN7tvG5dpLUGUtbXOf//dws/h/97S5fhv//+7/8rlp0iZJjZQ+4BAP0fxB+oxUzhEhCZFQgASCRJgKgIAgW5CI9hEgYUjb4vA040hA5MUlBKRpmampVmzLlgSYkesOvszRIzyBvG2McAspDGBAI4GJTnP/7kMT3gB9JiRv5nYBKwKFjPx+wAHeg44Xji1Zj4CDRh2ANDOWlTCZopfP32SxGEQ9eWU5JmSZkSIORmcMGcPVGgYyikhuQsNYW4brv4GCDPmxYmoInWzvfedprVbDGmtVZhrwQHM8QAxBNQDB2PyCknMeY/+WNe1Z33HvfL5vPjKIxKGds7afhP4d+fxzy5nZoN3JTuS0/cMFBGmSyV09Ortr8vjEY///evws////4WrN/faSiw5n//7iIruuvdBxNBs0MNiYgpvI+uI3////w/cnUI8ocKAEAAZAIYZSYIwDDBxXTKmT1ZAAwRrykMu0mLNnWjveYs0Y8k6ceEAAYmCPWIgGONWTuABOb0hVFZjHzh/iYABwufOEGyqCdSgxliaYQCubbNrijIwhST/JzKykIApqkU6zryWJZyx9VNrUFlzQsDmtniQycqYuCHEygBczLtYGCRnpeAhbGUluVhoadatUYtGZTO0uUuWL9iNU7pP8wkGgBgoQ0+LxWLX3BfzKrlZ5Vxs4ztjuGO889txVyyFwoeSqmaWxS6xpM8//7oMToACAVlx35rRIEETQj/zWwCOUuWOtfvm+ZZ93jzdt4nnb1kMUIAVCU41NWjUlj7y93jr8scccv/f6z//592l1z9/v7VNb41t13t32s+t2lbSSJIIRIpKSRx6tpbIjCLToxSeaLYPk6D/p1M3lVEZQGIQyTRcY0CV+UAJiRC6Q4YZdqcKiTDX4h41AExQEeCNXOsqGkD6t0nYZbC9UpotSyUQ1Codc3jstFXwaFItencSgySRpIPpXXYgHBKSVP5ZrZ6zm7Nikz3cqxjLv6v6qT1Fhnjn3uMj3zPeuXtyrG3lv/3X1X7//r+c/mWOeua/L+fh3uq/cuXe//////f////5+96u1TycBEFBvdbbndmncjAgCy2DERiwWkqiRoiEZMuGikhg4AYoCmfBBm5eZ0emIkgEBR0OAy+ABox8cPtzjHQZBGYVCIXCogA5g4StkEIEfswgHAcLTBALMBgoxmw0EwoGzMSlMNgtLBL9e5o5AGHw3FFAjFlTMLCxlMaR7MBgkIB5hIHGCAEAAOBgCh2TRMEAlYJPuPtZXS8ph4RGIhEDhQgPixh4UmCw4YbDJgIOgkDGBwBGZ8xCGmhZy1ghgsAoPr3RQcFa7XpqZ7DsdLyS3C1WpnSBoCMFgFgy1IxRT7+MAcQMQBk0EujDbWX9aM4z1mAwoTBIs7lTU2EuuI/yOYLP/7sMTgABihbRW4/QAF28IlfzfACJlm1/v+5brrochGYukAAYJAluQJAS8WuzsppcKBDKAp7Gta/9SCWVKRh6w73sTZ3I6S92WZqKpSiwGLSFAVTHLTKegx9KF8WolpUfoJm1ywwyJk2eFNlly5Wq///////////////////FMMM////////////////4CuZU2ry5i7uSA6j2+eFnqDqnQ8lK2CZfT0Xdv9GIPhuGYZeiGI00iK3X9abjvurVNJUeUAhnAg6pq7sV5/f+rLaWM3t3I1bv/+P48xvY0sZXKEBabGbOr2X/+/1rmW+83+WPavNVo1NW2krqbtI79qaqdyy/Hme+X9fq5rO3b7Uq2r0cxtalVbes7OM9P0fJJZpdY85rDOvfv4/l/Ocxq1aWljMZpsstb7vDDP7Vu6dOH21JSnVERMzVU5ie+6XaaKwnCseAtqtD2qxOEILYQBNj1G6SyGqaRzto9YncA/ien2Qc+rrtyOpvVNYdp2d/PQ3XU+Y+/bOtyzQHy7UJXElZZ5tsrVGjQazUx8bp/XO4W3tbRUAxpznecZ8F7nevve9rUpfFYHg17lvOIjkw23TEPN4u60Zp3sJxpn7ru0827V8e+s218ZxnVd7pCanNiVivZm2FE3q9M3veudVrjeb7v/SPeambq9lZkCASC9vSwSB1ah/090PzCYFlstapTAEAjH0NTGE8EVhAG5ggA5iWCJkA/ZpxUxiYCRhiIACC4gDQtFTlMcBoIIAivRLBdxgAYmHDSa6/j8kAEMzpYwYDFcP4yw+QbjFBzFmkEFA0+YjCo4Bwia1LE7AgAFYIWcZqQ5//uAxPCAFlVzJ/2MACK9tGR+nvAEqUoGcCCaIYZhNeBUBCQCxYdAydAcBFlr3Y+8hjlJmAxKaYBwKdZkMyGBiqFgCSgAwgIUxYBaIscu21Rgj8N3T7MBCAuwHComEJggJjRWM1GKXR5/n+yTeMKigeCZfBXbvx7GErTV+a6jBp4Eg4fFA7MAAYwGBxYVg4Qg4NLaLTMvmn+md/MvfORdg76PJOyp+HEj5ikRDghMECgIBBj0AgokGGAeYTAhUCIKBqU+EeZFDUdmJrTWn9dlqTvwVAivJFIIEd9yIhvKLOyiMYgMBkYTmhyuNNACgwWDJgYBgEBFABLABVEpYFgAQgGlbkgCV1cnssstZZVsu28+8////////////////rRbcb//BEJszwzuzOzg7ibfb62yNtJhVUUrUWsu4WVIRw9cc9WEtQj84D5bZ+5zDoGVsZqw6URUGJ5hYQAFQGrRakIKjHyY8pLNCJZMyw7/+7DE64Ax7dkh+d4AA9UzIv8xsAAeUBSUgTe0y7XMBHCIRpXaMNCxYUasyKSM6aREaaliEOy95QaEmElhyqEJFaCKKKApGlYNHoFlEojr6yybpZa+67k3C5ibocDSjGWO0/z+hQJwr1r2L7RtWOZV0wZkr0ymtKn77hZ1+d2pEOY49yw58zRyq7D8CP/Hp+UzMo73U3S5fr+ZUdzHvb+8cvabjHeNJh6JTlFj+X575/493l/////9z1jhySczx/v29Vq3S5enhlcQAABX15gIDB0lfCciZQUA921ul2zCkM0FoZupjTsKFSY0+ULemEC5gMAmCwmYcJ5kAmgUNGGQk64OChgEVqUGADyZkzaRw4DTOjlMPgMxCEUk5eaZ2xxcyGEQgYtEBzTymYgO1xWIu2uuLuW5aKBhsdGSzEYKFIOHidQwAy7MLi611NGWUS7FMC+SeogBpggFonAUAMCXag5A0Vf2KRfPTT2QMkU7a0zphxVABgUHGEBLS40tzGPFqggCN1YO1+X35fT3zI4UNVAYHJNNJWxU0H0MBAAEkoMMIgJpL8ynL7kUwxdhrDiZRyHIceuvLYFa9KmkwVEnxssphmHaWrATOrOU7HF3QPDcBMEREcutzOpbp8tbUMVYFAYYBD4gFIWBkMuHJ3Heh7EU2TPkw6o5UTaSoK03K1rlNHca3//P///////////////56GJFV////////////////pthUVMxMzcEHv7K3lvUqGD/Ti+oWuifZZEQfD9Kr7vOO4a9N1Zps2NLALtNeZc0piRhuHawZAYGVWbDENUcnyxdp3r7vRbHcczy7buaz//7oMTXgCxuDSn5vgJKkySkP5+QBHGaTtPYqsplsrm4ams/3ylt5Sv+P8/3ea/CSdrZ7x+ZlTky2X38uc3ZxmcJi3GsOb7XlnL9/X63Q3qaV9pvpqaxudsYR/CPUdg/U8spbIowUrAhADSh1aK0VZqXiquoIL7Sy3SsY2JbZM792wsLLFLAN1LE0QhKsLLVclnJjeWejDGTpjF0V5bE7JEpjd1E9PRx7t67hWk3eBDg1mjwWpIIpsrHw22zqA41bZfj5nz923Dlh2kkXBMj+WI8HbGxseLWgwIUt3OBakPFrb3H16t7na2Na+o0uN3rNX1tH3iXVIG6MDhSWvxqlt7jxKbh+9LZpesPd96prGdatSla0pSmsv36sVndX9ebeXSOCoH0+fAkGg9BhgoY0BGOpRwzaDFU20uQ3MWJDRhky1qKiOXGMWJjJAo0lQMZMQSRBRkMvlEyOKTBYyMnjox4VTVwSqGCQyBh2Y/Ghg1LmUnQWA0YCBBiWtFpDF4RKoAEgIb7SYhIhh0AKPmjwuY4EMh0YbBapkrDCIHQHmAw0YPByURdYEgRlr2W0OyYU8YTCJhMGo1mBwGYBAZgcGF7RwJGFQozlIkcApeZJ2OvZKy9YGBDTW3MBAIwAAFIUEljMVjSaCXE9OMaZMKggwqJDAYHTqMCAJoD+RiHH/f+RmYSEYGCpf19HP/7sMTNgBV9oyP094AmHsImvzfACG5yYZEYWE5eZfj+xjn6j8lcuB4IRUbxMRdj8QO0hzXkedL5YdDWLz7utaXdD0Tsu/YjL/XWcuE2Veixi9DW08C66fctht+7D6RRaCARuIQFzGo1MTBwwAAVWrGVLGaCMUzcVlMrMBBFWZjziL1gfWXZ2bkMfn+Xuc////////////////+dlvM////////////////5LM7f5d/MzNzEAHtqN9sifIeU1Nh2pGc4zZisCSmIOstpQJl0ZYK8MUyiT/P9WpaKVMCAIC0RjAaYGsSpcX2dqNX8efTVcJ2Uy2K0u7+OXcu67vVrKGlTO9DUqrXcbmss7mVb/yyyppdayx1KpdnXlMmmY3EXFd1/Wuu7g/07x/n+hqjxxx///fN1bNNa/v7q0tL3GrS83v8sccccf3ytKYzS2caWzWpv3j++Vn+h5/YdoJ6Ov7DuOOX/vHHHHH8ccf+Zhl/Y13HHHGZjMMy6VXdVV1kwAfmVtC4H++TbTMwuosNEGjdfwxicAmBCcbZkVSBkqWCLvoFEWcR4hQWWC8Tgt4kpaUbpkieImkVioShBi7SR2Q1mLqNyLDBG4QUhiZMJl9NMumqBdfUaoyezjGCyZHARYiRFRYxkSqTxmoaJVK5iakHWaMiUn84ifaormiBoXDZqL0CnKZvums+tKvQOumYMZv63pXmobHinmBICA/XQIXAkSUHYYga7Jp6GTyesTgJ1HAQeQx/Tgye+xA3eT9JIvDvBYKSAJgggdRAGLiAUC3hEYiZGPs0LomkLk+0gdJTAg4hH2P0g99GVDxjAtDIMGEoi//uQxOIAGX2jL/2MACJiqOT+nwAF6K0H3buv9WOmNBIQKJGXDxkRcCB97WHMLjSXr8IestL8LszSvMjES+iZhMMAUBbBDoYByFxYw5Etd516BuC6S46earXXaihmDgNV6dT1sQaTHl2KvksC2YZzbm473rwWYXbSHYw0BKxjary4yJEMWYeePbPXid+L134zlEP9/mRETskXWGB6Gjf0vf1SUDnl2Hnta///+Z/Yz7ykxt5/8vywUdQWL8Q0sUBC5h4KjOmepepmpMxUNKAbdj5XIJzKXTNyX9iDYIrhz/////////////////oVeY/////////////////2iub/P63MAwAALbNUhBkNSbSJZEIEVZ3N5AShOMwNMAV6sxTUVtZrStilWC7m2wmneUqaEvExEM2RcuMABbLmlQNMU8ZyluMa5e3ZlNjdjHHHeNmhnZp2X9mI6wLGzS4b7vHHGrhj3eOOOPP///dmDWuyhOZMWSR1drsw7S445Zf/4444/+OOP81KozZvRJ/oahq7OwzFZmlpaXH/s9/99//////5//ugxPWAKNoNO/mdpANAtKZ7saAAqVSmkhqRVbNa1nVs8q2e8//3jjjhlnWlMt1llTdqymmylTtO9Go0/0qpbOL//u7/xxE9UclCyQsA7mIlS2DZDUcCCZJLJM3CC3xZ7AhKIQ9ARIAdFwk9i1TAIxZLBJDbghNCgZCQpeeL66hFJqEhakp8zOu468tDoHQA99atv++8/qMsjOGZ6cDwFQVITQhFiICmVL89dm///lor1e5EqDKwKjpMoGkwDB0jkgQ6oiJkOs5/////sCWNoc9IirZLlWhjUJZX/8VXx/8v02v/QqmITMubebmbKCF1K1NKYBtialKwJR4oh1uCmdI9QkHSS7WGhPDqeuc6JiNsNTq9LCaCkXU6lZ0kokOUM1mJWEvV7C7mVqNdK7/+ZLWj6IiIDYbrGZDH1jjq/qRZIVnFcItXIUZImIhTIyYjC10fRMQci6f6qjm1I00uWUmmzUu3+WQ4URKTRqEC7Zsnenbmv1/7QU793//SVOZFJGPkByKENb/////zK7SOzdu8zJcQ+pW718WcSkAfB/0U6R6vZnJh0nHHT0/Rwj0sJysq9CgxemBJiHhwhumk4K46mZ8+fWsfuWpqcp1croUX/5x/Xtz9EikI5roxPYOoMX//9gqQD2N18Y5M1FuxqvMolTToud/RGVEkvyqjSLM/r860ZLBQCgOi//uAxOSAFP2fN+wxL6qXsmW89adM0RQei04kKhsNSP5p3/OJK1C41FJA443/8VA5MJO7s8M8RCBeSyAc0jLVWmkteZmCRKZFAJqwqGcihQlBUjBEoKsFT8//7yElLE00LK2fysMylB8DoaAWHqrSv//FkodNfK7Wq0te1z1rTObDesWqqgtTNMNK1KrAslFVTEFNRTMuMTAwVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX/+2DE6YATBZUt55j+oXUjI7xkoiVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVU=";
    const WATCHDOG_TIMEOUT = 500;   // seconds before forcing a check
    const WATCHDOG_INTERVAL = 100;  // how often watchdog scans

    /* ---------------- DEFAULT ALERTS ---------------- */

    const DEFAULT_ALERTS = {

        QUEUE_EMPTY: {
            enabled: true,
            sound: SOUND_DATA,
            soundType: "base64",
            volume: 0.7,
            speed: 1,
            message: "Queue Empty",
            initialDelay: 1000,
            repeat: 10000
        },

        ASYNC_UNARMED: {
            enabled: true,
            sound: SOUND_DATA,
            soundType: "base64",
            volume: 0.7,
            speed: 1,
            message: "Async Unarmed",
            repeat: 20000
        },

        LAYER_COMPLETE: {
            enabled: true,
            sound: SOUND_DATA,
            soundType: "base64",
            volume: 0.7,
            speed: 1,
            message: "Layer Completed",
            repeat: 20000
        },

        BONUS_TOOLTIP: {
            enabled: true,
            sound: SOUND_DATA,
            soundType: "base64",
            volume: 0.7,
            speed: 1,
            message: "Bonus Tooltip"
        }

    };
    const WATCHDOG = {
        checkQueue: { last: Date.now(), fn: checkQueue },
        checkAsync: { last: Date.now(), fn: checkAsync },
        checkLayer: { last: Date.now(), fn: checkLayer },
        checkBonus: { last: Date.now(), fn: checkBonus }
    };
    /* ---------------- UTIL ---------------- */

    const clone = v => JSON.parse(JSON.stringify(v));

    function gmGet(k,f){try{return GM_getValue(k,f);}catch{return f}}
    function gmSet(k,v){try{GM_setValue(k,v);}catch{}}

    /* ---------------- SETTINGS LOAD ---------------- */

    let ALERTS = gmGet(SETTINGS_KEY,null);

    if(!ALERTS){

        ALERTS = clone(DEFAULT_ALERTS);
        gmSet(SETTINGS_KEY,ALERTS);

    }else{

        for(const k in DEFAULT_ALERTS){

            if(!ALERTS[k]) ALERTS[k] = clone(DEFAULT_ALERTS[k]);

            for(const f in DEFAULT_ALERTS[k]){
                if(!(f in ALERTS[k])) ALERTS[k][f]=DEFAULT_ALERTS[k][f];
            }

        }

        gmSet(SETTINGS_KEY,ALERTS);

    }

    let saveTimer=null;

    function saveAlerts(){

        clearTimeout(saveTimer);

        saveTimer=setTimeout(()=>{

            gmSet(SETTINGS_KEY,ALERTS);

        },300);

    }

    /* ---------------- AUDIO CACHE ---------------- */

    const AUDIO_CACHE = new Map();

    function getAudio(src){

        if(!AUDIO_CACHE.has(src)){

            const audio = new Audio(src);
            audio.preload="auto";

            AUDIO_CACHE.set(src,audio);

        }

        return AUDIO_CACHE.get(src);

    }

    function playSound(alert,reason){

        try{

            const base = getAudio(alert.sound || SOUND_DATA);

            const audio = base.cloneNode();

            audio.volume = alert.volume ?? 1;
            audio.playbackRate = alert.speed ?? 1;

            audio.play().catch(console.warn);

            croaker(`[${alert.message}]`,1);

            log(`PLAY ${alert.message}${reason?` (${reason})`:''}`);

        }catch(e){

            console.warn(e);
            log("Audio error");

        }

    }

    /* ---------------- LOG ---------------- */

    const LOG=[];

    function log(msg){

        const line = new Date().toLocaleTimeString()+" "+msg;
        if (DEBUG) console.log(line);
        LOG.push(line);
        if(LOG.length>200)LOG.shift();

        const el=document.getElementById("croaker-log");
        if(el) el.textContent = LOG.slice().reverse().join("\n");

    }

    /* ---------------- CROAKER HEADER ---------------- */

    function croaker(message,delay){

        const anchor=document.querySelector("header.navbar div.flex-1 a");
        if(!anchor)return;

        let span=anchor.querySelector("span.croaker");

        if(!span){

            span=document.createElement("span");
            span.className="croaker";
            span.className="valuable";
            anchor.appendChild(span);

        }

        if(span.textContent) return;

        span.textContent=message;

        setTimeout(()=>{

            if(span.textContent===message) span.textContent="";

        },delay*1000);

    }

    /* ---------------- ALERT STATE ---------------- */

    const ALERT_STATE={};

    for(const k in ALERTS){

        ALERT_STATE[k]={active:false,timer:null,repeat:null};

    }

    let bonusArmed=true;

    /* ---------------- ALERT CONTROL ---------------- */

    function startAlert(key){

        const a=ALERTS[key];
        const s=ALERT_STATE[key];

        if(!a.enabled || s.active) return;

        s.active=true;

        const fire=()=>playSound(a,key);

        if(a.initialDelay){

            s.timer=setTimeout(()=>{

                fire();

                if(a.repeat) s.repeat=setInterval(fire,a.repeat);

            },a.initialDelay);

        }else{

            fire();

            if(a.repeat) s.repeat=setInterval(fire,a.repeat);

        }

        log("START "+key);

    }

    function stopAlert(key) {
        const s = ALERT_STATE[key];
        if (!s || (!s.timer && !s.repeat && !s.active)) return; // skip if nothing to stop

        if (s.timer) clearTimeout(s.timer);
        if (s.repeat) clearInterval(s.repeat);

        s.timer = null;
        s.repeat = null;
        s.active = false;

        log("STOP " + key);
    }
    /*    function stopAlert(key){

        const s=ALERT_STATE[key];

        if(s.timer)clearTimeout(s.timer);
        if(s.repeat)clearInterval(s.repeat);

        s.timer=null;
        s.repeat=null;
        s.active=false;

        log("STOP "+key);

    }
*/
    /* ---------------- CONDITIONS ---------------- */

    function queueEmpty(){

        const t=document.title.toLowerCase();
        return !t.includes("queue") && !t.includes("async") ;

    }

    function asyncActive(){
        const t=document.title.toLowerCase();
        return t.includes("async");
    }

    function countTileWrappers() {
        return document.querySelectorAll('div[id^="tile_wrapper_"]').length;
    }

    function countQueuedTiles() {
        return document.querySelectorAll('div[id^="tile_inner_"].you-are-queued-here').length;
    }

    function countMiningTiles() {
        return document.querySelectorAll('div[id^="tile_inner_"].you-are-mining-here').length;
    }

    function layerComplete() {
        return document.querySelector('div#layer-complete-text') !== null
    }

    function checkLayer() {
        if (DEBUG) console.log('------- layerComplete - ' + layerComplete());
        if (layerComplete()) {
            startAlert("LAYER_COMPLETE");
        } else {
            stopAlert("LAYER_COMPLETE");
        }
    }

    function checkQueue(){
        if (DEBUG) console.log(' ===== check queue');
        //console.log('CkQ  layer-' + layerComplete() + ' queueempty-' + queueEmpty() + ' async-' + asyncActive() + ' queuedtiles-' + countQueuedTiles() + ' miningtiles-' + countMiningTiles() + ' tilescnt-' + countTileWrappers());

        if((queueEmpty() && countTileWrappers() > 0 && countMiningTiles() == 0) && !layerComplete() ) startAlert("QUEUE_EMPTY");
        else stopAlert("QUEUE_EMPTY");
    }

    /*     function checkAsync(){
        if (DEBUG) console.log(' //// check async');
        const el=document.querySelector('span[data-role="auto-async-indicator"]');
        if(!el)return;

        const t=document.title.toLowerCase();

        if(el.classList.contains("hidden") && !t.includes("async"))
            startAlert("ASYNC_UNARMED");
        else
            stopAlert("ASYNC_UNARMED");

    }
 */

    function checkAsync(){
        if (DEBUG) console.log(' //// check async');

        const header = document.querySelector('div#auto-async-header');
        if (!header) return;

        const hasPending = Array.from(header.querySelectorAll('span'))
        .some(s => s.textContent.includes('[PENDING]'));

        const t = document.title.toLowerCase();

        if (!hasPending && !t.includes("async")) {
            startAlert("ASYNC_UNARMED");
        } else {
            stopAlert("ASYNC_UNARMED");
        }
    }
    function checkBonus(){
        if (DEBUG) console.log(' @@@@ check bonus');
        const panel=document.querySelector("turbo-frame#bonus-panel");
        if(!panel)return;

        const tooltip=panel.querySelector("div.tooltip:not(.bonus-active)");

        if(tooltip){
            if(bonusArmed){
                playSound(ALERTS.BONUS_TOOLTIP,"BONUS");
                bonusArmed=false;
            }
        }else{
            bonusArmed=true;
        }
    }

    /* ---------------- UI ---------------- */

    GM_addStyle(`
#croaker-gear{position:fixed;bottom:10px;left:10px;background:#222;color:#fff;padding:6px;border-radius:6px;cursor:pointer;z-index:9999}
#croaker-panel{position:fixed;bottom:50px;left:10px;background:#111;color:#eee;padding:10px;border-radius:8px;width:320px;max-height:60vh;overflow:auto;z-index:9999}
#croaker-panel input[type=range]{width:120px}
#croaker-log{white-space:pre;font-family:monospace;font-size:11px;background:#000;padding:6px;margin-top:6px;max-height:150px;overflow:auto}
button{cursor:pointer}
`);

    function buildUI(){

        const gear=document.createElement("div");
        gear.id="croaker-gear";
        gear.textContent="⚙";

        const panel=document.createElement("div");
        panel.id="croaker-panel";
        panel.style.display="none";

        document.body.appendChild(gear);
        document.body.appendChild(panel);

        gear.onclick=()=>{

            panel.style.display = panel.style.display==="none"?"block":"none";

        };

        for(const key in ALERTS){

            const a=ALERTS[key];

            const row=document.createElement("div");

            row.innerHTML=`
<label>
<input type="checkbox" ${a.enabled?"checked":""}> <b>${key}</b>
</label>

<br>vol <input type="range" min="0" max="1" step="0.05" value="${a.volume}">
<br>spd <input type="range" min="0.5" max="2" step="0.1" value="${a.speed}">
<button class="test">Test</button>

<div>
<input class="url" placeholder="Sound URL" style="width:90%">
<input type="file" class="file" accept="audio/*">
<button class="default">Default</button>
</div>
<br>
`;

            const checkbox=row.querySelector("input[type=checkbox]");
            const sliders=row.querySelectorAll("input[type=range]");
            const test=row.querySelector(".test");
            const url=row.querySelector(".url");
            const file=row.querySelector(".file");
            const def=row.querySelector(".default");

            checkbox.onchange=()=>{

                a.enabled=checkbox.checked;
                saveAlerts();

            };

            sliders[0].oninput=()=>{

                a.volume=parseFloat(sliders[0].value);
                saveAlerts();

            };

            sliders[1].oninput=()=>{

                a.speed=parseFloat(sliders[1].value);
                saveAlerts();

            };

            test.onclick=()=>playSound(a,"TEST");

            url.onchange=()=>{

                a.sound=url.value.trim();
                a.soundType="url";

                saveAlerts();
                log(key+" URL sound");

            };

            file.onchange=()=>{

                const reader=new FileReader();

                reader.onload=e=>{

                    a.sound=e.target.result;
                    a.soundType="file";

                    AUDIO_CACHE.delete(a.sound);

                    saveAlerts();
                    log(key+" file sound");

                };

                reader.readAsDataURL(file.files[0]);

            };

            def.onclick=()=>{

                a.sound=DEFAULT_ALERTS[key].sound;
                a.soundType="base64";

                saveAlerts();
                log(key+" default sound");

            };

            panel.appendChild(row);

        }

        const logBox=document.createElement("div");
        logBox.id="croaker-log";
        panel.appendChild(logBox);

    }

    setInterval(()=>{
        const now = Date.now();
        for(const key in WATCHDOG){
            const w = WATCHDOG[key];
            if(now - w.last > WATCHDOG_TIMEOUT){
                log("WATCHDOG fired: "+key);
                w.last = now;
                try{
                    w.fn();
                }catch(e){
                    console.warn("watchdog error",key,e);
                }
            }
        }
    }, WATCHDOG_INTERVAL);
    /* ---------------- INIT ---------------- */

    buildUI();

    checkQueue();
    checkAsync();
    checkBonus();
    checkLayer();

    log("Croaker ready");

})();