// ==UserScript==
// @name IQRPG+ Enhanced
// @namespace https://www.iqrpg.com/
// @version 0.2.0
// @description Audio signals for various aspects of IQRPG with enhanced GUI
// @Author Bunjo & vifs, enhanced by Sanjin
// @match http://iqrpg.com/game.html
// @match https://iqrpg.com/game.html
// @match http://www.iqrpg.com/game.html
// @match https://www.iqrpg.com/game.html
// @require http://code.jquery.com/jquery-latest.js
// @grant none
// @license MIT
// @homepageURL https://github.com/yourusername/iqrpg-plus
// @supportURL https://github.com/yourusername/iqrpg-plus/issues
// ==/UserScript==
/*
Shout out to Xortrox & euphone & Karubo for their contributions to the script.
Enhanced by: Sanjin
*/
// Global variables for script outside the namespace
let globalUserSettings = {};
const OldSocket = window.WebSocket;
// WebSocket interception at top level for immediate execution
window.WebSocket = function WebSocket(url, protocols) {
const socket = new OldSocket(...arguments);
window.socket = socket;
socket.addEventListener('message', function(event) {
try {
const message = JSON.parse(event.data);
switch(message.type){
case 'playersOnline':
case 'loadMessages':
case 'addItemsToUser':
case 'notification':
case 'bonus':
break;
case 'event':
// Handle resource events (woodcutting, mining, quarrying)
if (!message.data || !message.data.type) return;
const eventType = message.data.type;
const timeRemaining = message.data.timeRemaining || 0;
if (eventType === "woodcutting" && globalUserSettings.eventAlert_Woodcutting) {
// Handle woodcutting event
if (globalUserSettings.eventAudioAlert) {
window.IQRPG_Plus.playSound(globalUserSettings.eventAlertSoundURL);
}
if (globalUserSettings.eventDesktopAlert) {
window.IQRPG_Plus.showNotification('IQRPG Event!', 'Woodcutting event has started!');
}
// Set timeout for end of event
setTimeout(function() {
if (globalUserSettings.eventAudioAlertFinished) {
window.IQRPG_Plus.playSound(globalUserSettings.eventAlertSoundURL);
}
if (globalUserSettings.eventDesktopAlert) {
window.IQRPG_Plus.showNotification('IQRPG Event Finished!', 'Woodcutting event has ended!');
}
}, timeRemaining * 10);
}
else if (eventType === "mining" && globalUserSettings.eventAlert_Mining) {
// Handle mining event
if (globalUserSettings.eventAudioAlert) {
window.IQRPG_Plus.playSound(globalUserSettings.eventAlertSoundURL);
}
if (globalUserSettings.eventDesktopAlert) {
window.IQRPG_Plus.showNotification('IQRPG Event!', 'Mining event has started!');
}
// Set timeout for end of event
setTimeout(function() {
if (globalUserSettings.eventAudioAlertFinished) {
window.IQRPG_Plus.playSound(globalUserSettings.eventAlertSoundURL);
}
if (globalUserSettings.eventDesktopAlert) {
window.IQRPG_Plus.showNotification('IQRPG Event Finished!', 'Mining event has ended!');
}
}, timeRemaining * 10);
}
else if (eventType === "quarrying" && globalUserSettings.eventAlert_Quarrying) {
// Handle quarrying event
if (globalUserSettings.eventAudioAlert) {
window.IQRPG_Plus.playSound(globalUserSettings.eventAlertSoundURL);
}
if (globalUserSettings.eventDesktopAlert) {
window.IQRPG_Plus.showNotification('IQRPG Event!', 'Quarrying event has started!');
}
// Set timeout for end of event
setTimeout(function() {
if (globalUserSettings.eventAudioAlertFinished) {
window.IQRPG_Plus.playSound(globalUserSettings.eventAlertSoundURL);
}
if (globalUserSettings.eventDesktopAlert) {
window.IQRPG_Plus.showNotification('IQRPG Event Finished!', 'Quarrying event has ended!');
}
}, timeRemaining * 10);
}
break;
case 'msg':
// Handle different types of chat messages
if (!message.data || !message.data.type) return;
switch(message.data.type) {
case 'clanGlobal':
if (message.data.msg && message.data.msg.startsWith('The watchtower')) {
if (globalUserSettings.watchtowerAudioAlert) {
window.IQRPG_Plus.playSound(globalUserSettings.watchtowerAlertSoundURL);
}
if (globalUserSettings.watchtowerDesktopAlert) {
window.IQRPG_Plus.showNotification('IQRPG Watchtower!', message.data.msg);
}
}
break;
case 'pm-from':
if (globalUserSettings.whisperAlertOnlyWhenTabIsInactive) {
if (document.hidden) {
if (globalUserSettings.whisperAudioAlert) {
window.IQRPG_Plus.playSound(globalUserSettings.whisperAlertSoundURL);
}
if (globalUserSettings.whisperDesktopAlert) {
window.IQRPG_Plus.showNotification('IQRPG Whisper!', message.data.username + ': ' + message.data.msg);
}
}
} else {
if (globalUserSettings.whisperAudioAlert) {
window.IQRPG_Plus.playSound(globalUserSettings.whisperAlertSoundURL);
}
if (globalUserSettings.whisperDesktopAlert) {
window.IQRPG_Plus.showNotification('IQRPG Whisper!', message.data.username + ': ' + message.data.msg);
}
}
break;
case 'eventGlobal':
// Boss event detection
if (message.data.msg && message.data.msg.includes('A rift to the dark realm has opened')) {
if (globalUserSettings.bossAudioAlert) {
window.IQRPG_Plus.playSound(globalUserSettings.bossAlertSoundURL);
}
if (globalUserSettings.bossDesktopAlert) {
window.IQRPG_Plus.showNotification('IQRPG Boss!', message.data.msg);
}
}
break;
case 'pm-to':
case 'msg':
case 'global':
case 'me':
break;
}
break;
case 'boss':
break;
}
} catch (error) {
// Silent fail
}
});
return socket;
};
// Use a namespace to avoid global scope pollution
window.IQRPG_Plus = (function() {
// Private variables
let userSettings = {};
let isAlerting = false;
let alertTimer = null;
let currAutoAudioPlays = 0;
let desktopNotificationOnCooldown = false;
let bonusExp = false;
let isDragging = false;
let dragOffsetX = 0;
let dragOffsetY = 0;
// Store references to UI elements
const elements = {
buttonPanel: null,
buttonContainer: null,
settingsPanel: null,
settingsButton: null,
};
// Store references to observers
const observers = {
main: null,
land: null,
mastery: null
};
// Default settings
const defaultSettings = {
masterAudioLevel: 1,
autoAudioAlert: true,
autoAlertSoundURL: 'https://www.pacdv.com/sounds/mechanical_sound_effects/gun-reload-1.wav',
autoAlertRepeatInSeconds: 2,
autoAlertNumber: 10,
autoMaxNumberOfAudioAlerts: 2,
autoDesktopAlert: true,
dungeonAudioAlert: true,
dungeonDesktopAlert: true,
bossAudioAlert: true,
bossAlertSoundURL: 'https://www.pacdv.com/sounds/interface_sound_effects/sound8.mp3',
bossDefeatedSoundURL: 'https://ia801306.us.archive.org/32/items/FF7ACVictoryFanfareRingtoneperfectedMp3/FF7%20AC%20Victory%20Fanfare%20Ringtone%20%28perfected%20mp3%29.mp3',
bossDesktopAlert: true,
eventDesktopAlert: true,
eventAlertSoundURL: 'https://www.pacdv.com/sounds/interface_sound_effects/sound8.mp3',
eventAlert_Woodcutting: true,
eventAlert_Quarrying: true,
eventAlert_Mining: true,
eventAudioAlert: true,
eventAudioAlertFinished: true,
whisperAudioAlert: true,
whisperAlertSoundURL: 'https://www.pacdv.com/sounds/mechanical_sound_effects/spring_1.wav',
whisperAlertOnlyWhenTabIsInactive: false,
whisperDesktopAlert: true,
landAudioAlert: true,
landAlertSoundURL: 'https://www.pacdv.com/sounds/mechanical_sound_effects/coins_4.wav',
masteryAudioAlert: true,
masteryEveryXLevels: 50,
masteryAlertSoundURL: 'https://ia801306.us.archive.org/32/items/FF7ACVictoryFanfareRingtoneperfectedMp3/FF7%20AC%20Victory%20Fanfare%20Ringtone%20%28perfected%20mp3%29.mp3',
effectAudioAlert: true,
effectAutoLeft: 5,
effectAlertSoundURL: 'https://www.pacdv.com/sounds/mechanical_sound_effects/hammer-1.mp3',
watchtowerAudioAlert: true,
watchtowerAlertSoundURL: 'https://www.pacdv.com/sounds/interface_sound_effects/sound8.mp3',
watchtowerDesktopAlert: true,
bonusExpAudioAlert: true,
bonusExpAlertSoundURL: 'https://www.pacdv.com/sounds/miscellaneous_sounds/magic-wand-1.wav'
};
// Load settings from localStorage
function loadSettings() {
try {
const savedSettings = JSON.parse(localStorage.getItem('iqrpgSettings'));
const result = savedSettings ? Object.assign({}, defaultSettings, savedSettings) : defaultSettings;
// Copy settings to global variable for WebSocket access
globalUserSettings = Object.assign({}, result);
return result;
} catch (e) {
// Copy settings to global variable for WebSocket access
globalUserSettings = Object.assign({}, defaultSettings);
return defaultSettings;
}
}
// Save settings to localStorage
function saveSettings() {
try {
localStorage.setItem('iqrpgSettings', JSON.stringify(userSettings));
// Update global settings for WebSocket access
globalUserSettings = Object.assign({}, userSettings);
} catch (e) {
// Silently fail if saving fails
}
}
// Apply settings to checkboxes
function applySettingsToUI() {
// Apply checkbox states
for (const setting in userSettings) {
const checkbox = document.getElementById(setting);
if (checkbox && checkbox.type === 'checkbox') {
checkbox.checked = userSettings[setting];
}
}
// Apply volume slider
const volumeSlider = document.getElementById('volumeSlider');
if (volumeSlider) {
volumeSlider.value = userSettings.masterAudioLevel;
}
}
// Initialize the userscript
function init() {
userSettings = loadSettings();
// Ensure we have up-to-date settings
saveSettings();
createUI();
setupObservers();
setupEventListeners();
// Check Notification permissions
if (Notification.permission !== "denied") {
Notification.requestPermission();
}
}
// Create and setup UI elements
function createUI() {
// Create main panel
elements.buttonPanel = document.createElement('div');
elements.buttonPanel.id = 'buttonPanel';
// Create settings panel
elements.settingsPanel = document.createElement('div');
elements.settingsPanel.id = 'settingsPanel';
elements.settingsPanel.className = 'hidden';
elements.settingsPanel.innerHTML = `
<h2>Settings</h2>
<label>Master Volume: <input type="range" id="volumeSlider" min="0" max="1" step="0.1" value="${userSettings.masterAudioLevel}"></label>
<button id="resetSettingsButton">Reset Settings</button>
<div style="margin-top: 10px;">
<label style="display: block;"><input type="checkbox" id="autoAudioAlert" ${userSettings.autoAudioAlert ? 'checked' : ''}> Auto Audio Alert</label>
<label style="display: block;"><input type="checkbox" id="autoDesktopAlert" ${userSettings.autoDesktopAlert ? 'checked' : ''}> Auto Desktop Alert</label>
<label style="display: block;"><input type="checkbox" id="dungeonAudioAlert" ${userSettings.dungeonAudioAlert ? 'checked' : ''}> Dungeon Audio Alert</label>
<label style="display: block;"><input type="checkbox" id="dungeonDesktopAlert" ${userSettings.dungeonDesktopAlert ? 'checked' : ''}> Dungeon Desktop Alert</label>
<label style="display: block;"><input type="checkbox" id="bossAudioAlert" ${userSettings.bossAudioAlert ? 'checked' : ''}> Boss Audio Alert</label>
<label style="display: block;"><input type="checkbox" id="bossDesktopAlert" ${userSettings.bossDesktopAlert ? 'checked' : ''}> Boss Desktop Alert</label>
<label style="display: block;"><input type="checkbox" id="eventAudioAlert" ${userSettings.eventAudioAlert ? 'checked' : ''}> Event Audio Alert</label>
<label style="display: block;"><input type="checkbox" id="eventAudioAlertFinished" ${userSettings.eventAudioAlertFinished ? 'checked' : ''}> Event Finished Audio Alert</label>
<label style="display: block;"><input type="checkbox" id="eventDesktopAlert" ${userSettings.eventDesktopAlert ? 'checked' : ''}> Event Desktop Alert</label>
<label style="display: block;"><input type="checkbox" id="whisperAudioAlert" ${userSettings.whisperAudioAlert ? 'checked' : ''}> Whisper Audio Alert</label>
<label style="display: block;"><input type="checkbox" id="whisperDesktopAlert" ${userSettings.whisperDesktopAlert ? 'checked' : ''}> Whisper Desktop Alert</label>
<label style="display: block;"><input type="checkbox" id="landAudioAlert" ${userSettings.landAudioAlert ? 'checked' : ''}> Land Audio Alert</label>
<label style="display: block;"><input type="checkbox" id="masteryAudioAlert" ${userSettings.masteryAudioAlert ? 'checked' : ''}> Mastery Audio Alert</label>
<label style="display: block;"><input type="checkbox" id="effectAudioAlert" ${userSettings.effectAudioAlert ? 'checked' : ''}> Effect Audio Alert</label>
<label style="display: block;"><input type="checkbox" id="watchtowerAudioAlert" ${userSettings.watchtowerAudioAlert ? 'checked' : ''}> Watchtower Audio Alert</label>
<label style="display: block;"><input type="checkbox" id="watchtowerDesktopAlert" ${userSettings.watchtowerDesktopAlert ? 'checked' : ''}> Watchtower Desktop Alert</label>
<label style="display: block;"><input type="checkbox" id="bonusExpAudioAlert" ${userSettings.bonusExpAudioAlert ? 'checked' : ''}> Bonus Exp Audio Alert</label>
</div>
`;
// Create buttons with helper function
elements.settingsButton = createControlButton('🔔');
// Create container for buttons
elements.buttonContainer = document.createElement('div');
elements.buttonContainer.id = 'buttonContainer';
// Assemble the UI
elements.buttonContainer.appendChild(elements.settingsButton);
elements.buttonPanel.appendChild(elements.buttonContainer);
elements.buttonPanel.appendChild(elements.settingsPanel);
document.body.appendChild(elements.buttonPanel);
// Add stylesheets
addStyles();
// Apply saved position
loadSavedPosition();
// Initialize settings based on saved values
applySettingsToUI();
}
// Helper to create control buttons
function createControlButton(icon) {
const button = document.createElement('button');
button.textContent = icon;
button.className = 'control-button';
return button;
}
// Add CSS styles
function addStyles() {
const style = document.createElement('style');
style.textContent = `
#buttonPanel {
position: fixed;
top: 50px;
right: 10px;
z-index: 1001;
background-color: rgba(0, 0, 0, 0.8);
border: 1px solid #333;
padding: 0;
display: flex;
flex-direction: column;
cursor: move;
overflow: visible;
width: 120px;
transition: box-shadow 0.2s ease;
}
#buttonPanel.dragging {
box-shadow: 0 0 8px rgba(34, 116, 34, 0.7);
opacity: 0.9;
}
#buttonContainer {
display: flex;
flex-direction: row;
padding: 6px;
width: 100%;
box-sizing: border-box;
justify-content: center;
cursor: move;
}
.control-button {
background-color: rgba(0, 0, 0, 0.8);
color: white;
border: 1px solid #333;
padding: 8px;
width: 25px;
height: 25px;
line-height: 1;
text-align: center;
cursor: pointer;
font-size: 14px;
margin: 0 2px;
display: flex;
align-items: center;
justify-content: center;
}
.control-button:hover {
border-color: #227422;
background-color: rgba(10, 10, 10, 0.9);
}
#settingsPanel {
position: absolute;
top: 100%;
left: -1px;
width: calc(100% + 2px);
min-width: 250px;
background-color: rgba(0, 0, 0, 0.8);
color: white;
border: 1px solid #333;
border-top: none;
padding: 15px;
display: none;
z-index: 1002;
font-family: 'Arial', sans-serif;
box-sizing: border-box;
max-height: 80vh;
overflow-y: auto;
}
#resetSettingsButton {
background-color: rgba(50, 10, 10, 0.8);
color: white;
border: 1px solid #333;
padding: 5px 10px;
cursor: pointer;
font-size: 12px;
margin: 5px 0;
}
#resetSettingsButton:hover {
border-color: #227422;
background-color: rgba(10, 10, 10, 0.9);
}
#volumeSlider {
margin: 10px 0;
width: 100%;
accent-color: #227422;
}
#settingsPanel h2 {
margin-top: 0;
padding-top: 0;
font-size: 16px;
margin-bottom: 10px;
color: #227422;
}
#settingsPanel label {
font-size: 14px;
}
#settingsPanel input[type="checkbox"] {
accent-color: #227422;
}
`;
document.head.appendChild(style);
}
// Load saved position from localStorage
function loadSavedPosition() {
try {
const savedPosition = localStorage.getItem('iqrpgPanelPosition');
if (savedPosition) {
const position = JSON.parse(savedPosition);
elements.buttonPanel.style.left = position.left;
elements.buttonPanel.style.top = position.top;
elements.buttonPanel.style.right = 'auto'; // Clear right positioning when using left
} else {
// Initial position if no saved position exists
elements.buttonPanel.style.top = '50px';
elements.buttonPanel.style.right = '10px';
}
} catch (e) {
// Use default position on error
elements.buttonPanel.style.top = '50px';
elements.buttonPanel.style.right = '10px';
}
}
// Setup event listeners for UI elements
function setupEventListeners() {
// Settings button
elements.settingsButton.addEventListener('click', (e) => {
e.stopPropagation(); // Prevent triggering drag when clicking button
toggleSettingsPanel();
});
// Reset settings button
const resetSettingsButton = document.getElementById('resetSettingsButton');
if (resetSettingsButton) {
resetSettingsButton.addEventListener('click', () => {
if (confirm('Are you sure you want to reset all settings to default?')) {
localStorage.removeItem('iqrpgSettings');
userSettings = Object.assign({}, defaultSettings);
applySettingsToUI();
saveSettings();
alert('Settings have been reset to default.');
}
});
}
// Volume slider
const volumeSlider = document.getElementById('volumeSlider');
if (volumeSlider) {
volumeSlider.addEventListener('input', (event) => {
userSettings.masterAudioLevel = event.target.value;
saveSettings();
});
}
// Checkbox settings
setupSettingsToggleListeners();
// Make panel draggable
setupPanelDragging();
// Start/stop dungeon button
$('body').on('click', "button:contains('Start Dungeon')", function() {
stopAlert();
});
// Keyboard shortcuts
setupKeyboardShortcuts();
}
// Show/hide settings panel
function toggleSettingsPanel() {
if (elements.settingsPanel.style.display === 'none' || elements.settingsPanel.style.display === '') {
updateSettingsPanelPosition();
elements.settingsPanel.style.display = 'block';
} else {
elements.settingsPanel.style.display = 'none';
}
}
// Graph panel implementation placeholder
function showGraphPanel() {
// To be implemented
}
// Combat panel implementation placeholder
function showCombatPanel() {
// To be implemented
}
// Setup toggle listeners for settings checkboxes
function setupSettingsToggleListeners() {
const toggleSettings = [
'autoAudioAlert', 'autoDesktopAlert', 'dungeonAudioAlert', 'dungeonDesktopAlert',
'bossAudioAlert', 'bossDesktopAlert', 'eventAudioAlert', 'eventDesktopAlert',
'whisperAudioAlert', 'whisperDesktopAlert', 'landAudioAlert', 'masteryAudioAlert',
'effectAudioAlert', 'watchtowerAudioAlert', 'watchtowerDesktopAlert', 'bonusExpAudioAlert',
'eventAudioAlertFinished'
];
toggleSettings.forEach(setting => {
const checkbox = document.getElementById(setting);
if (checkbox) {
checkbox.addEventListener('change', (event) => {
userSettings[setting] = event.target.checked;
saveSettings();
});
}
});
}
// Setup panel dragging functionality
function setupPanelDragging() {
elements.buttonPanel.addEventListener('mousedown', (e) => {
// Only start dragging if clicking on the panel background or button container
if (e.target === elements.buttonPanel || e.target === elements.buttonContainer) {
isDragging = true;
dragOffsetX = e.clientX - elements.buttonPanel.getBoundingClientRect().left;
dragOffsetY = e.clientY - elements.buttonPanel.getBoundingClientRect().top;
elements.buttonPanel.classList.add('dragging');
}
});
document.addEventListener('mousemove', (e) => {
if (isDragging) {
const newX = e.clientX - dragOffsetX;
const newY = e.clientY - dragOffsetY;
// Calculate limits to keep panel within screen bounds
const maxX = window.innerWidth - elements.buttonPanel.offsetWidth;
const maxY = window.innerHeight - elements.buttonPanel.offsetHeight;
// Apply position with limits
elements.buttonPanel.style.left = Math.max(0, Math.min(maxX, newX)) + 'px';
elements.buttonPanel.style.top = Math.max(0, Math.min(maxY, newY)) + 'px';
elements.buttonPanel.style.right = 'auto'; // Clear right positioning
// If panel is near the right edge, reposition the settings panel
updateSettingsPanelPosition();
}
});
document.addEventListener('mouseup', () => {
if (isDragging) {
isDragging = false;
elements.buttonPanel.classList.remove('dragging');
// Save panel position to localStorage for persistence
const position = {
left: elements.buttonPanel.style.left,
top: elements.buttonPanel.style.top
};
localStorage.setItem('iqrpgPanelPosition', JSON.stringify(position));
}
});
// Stop dragging if mouse leaves the window
document.addEventListener('mouseleave', () => {
if (isDragging) {
isDragging = false;
elements.buttonPanel.classList.remove('dragging');
}
});
}
// Update settings panel position based on button panel position
function updateSettingsPanelPosition() {
const panelRect = elements.buttonPanel.getBoundingClientRect();
const screenWidth = window.innerWidth;
// If panel is close to the right edge of the screen, position settings to the left
if (panelRect.right + 250 > screenWidth) { // 250px is min-width of settings panel
elements.settingsPanel.style.left = 'auto';
elements.settingsPanel.style.right = '-1px';
} else {
elements.settingsPanel.style.left = '-1px';
elements.settingsPanel.style.right = 'auto';
}
// Match the width to the button panel
elements.settingsPanel.style.width = (elements.buttonPanel.offsetWidth + 2) + 'px';
}
// Setup keyboard shortcuts
function setupKeyboardShortcuts() {
document.addEventListener('keyup', function(e) {
if (e.altKey === true) {
let index = -1;
const channels = $('.chat-channels').children();
if (isNaN(e.key)) {
let direction = null;
if (e.which === 38) { // Up arrow
direction = true;
} else if (e.which === 40) { // Down arrow
direction = false;
}
if (direction !== null) {
let chatChannels = channels;
if (e.shiftKey === true) {
chatChannels = $('.chat-channels').children('.new-message, .active-channel');
}
index = chatChannels.index($('.active-channel'));
if (direction) {
index--;
} else {
index++;
}
}
} else {
if (e.ctrlKey === true) {
index = -1 + parseInt(e.key);
}
}
if (index >= 0 && index < channels.length) {
channels[index].click();
$('#chatInput').focus();
}
}
});
}
// Setup observers for DOM changes
function setupObservers() {
// Main observer for title changes
const titleTarget = document.querySelector('title');
if (titleTarget) {
let lastTitle = document.title; // Track last title to prevent duplicate processing
observers.main = new MutationObserver(function(mutations) {
const currentTitle = document.title;
// Only process if title actually changed
if (currentTitle !== lastTitle) {
lastTitle = currentTitle;
handleTitleChange(currentTitle);
}
});
observers.main.observe(titleTarget, {
characterData: true,
childList: true,
subtree: true
});
}
// Set up observer for land timers with delay
setTimeout(function() {
// Try different selectors that might contain the timer
const actionTimer = document.querySelector("div.action-timer__text") ||
document.querySelector(".timer") ||
document.querySelector("[class*='timer']");
if (actionTimer) {
observers.land = new MutationObserver(handleLandMutations);
observers.land.observe(actionTimer, {
characterData: true,
childList: true,
subtree: true
});
}
// Setup mastery observer
const masteries = document.querySelectorAll(".clickable > .flex.space-between > .green-text") ||
document.querySelectorAll("[class*='mastery-level']") ||
document.querySelectorAll("[class*='level']");
if (masteries && masteries.length > 0) {
observers.mastery = new MutationObserver(handleMasteryMutations);
masteries.forEach(function(mastery) {
observers.mastery.observe(mastery, {
characterData: true,
childList: true,
subtree: true
});
});
}
// Set interval for checking effects and bonus exp
setInterval(function() {
checkEffects();
checkBonusExp();
}, 5000);
}, 2000); // Increased delay to ensure elements are loaded
}
// Handle auto remaining changes
function handleAutoRemaining(data) {
if (!data) return;
try {
const autosRemaining = parseInt(data.replace('Autos Remaining: ', ''));
if (isNaN(autosRemaining)) return;
if (autosRemaining <= userSettings.autoAlertNumber && userSettings.autoAlertNumber) {
if (autosRemaining == userSettings.autoAlertNumber && userSettings.autoDesktopAlert) {
showNotification('IQRPG Auto Alert!', 'You have ' + userSettings.autoAlertNumber + ' remaining!');
}
// Only start sound alerts if auto audio alerts are enabled
if (!isAlerting && userSettings.autoAudioAlert && canPlayMoreAlerts()) {
startAlert();
}
} else {
stopAlert();
currAutoAudioPlays = 0;
}
} catch (error) {
// Silently fail if there's an error
}
}
// Handle title changes
function handleTitleChange(title) {
switch (title) {
case 'Dungeon Complete Idle Quest RPG':
if (userSettings.dungeonDesktopAlert) {
showNotification('IQRPG Dungeon Alert!', 'You have completed your dungeon!');
}
if (!isAlerting && userSettings.dungeonAudioAlert && canPlayMoreAlerts()) {
startAlert();
}
break;
case 'Clan Boss Defeated Idle Quest RPG':
if (userSettings.watchtowerDesktopAlert) {
showNotification('IQRPG Watchtower Alert!', 'Your clan has defeated the boss!');
}
if (userSettings.watchtowerAudioAlert && canPlayMoreAlerts()) {
playSound(userSettings.bossDefeatedSoundURL);
}
break;
case 'All Mobs Defeated Idle Quest RPG':
if (userSettings.watchtowerDesktopAlert) {
showNotification('IQRPG Watchtower Alert!', 'All mobs have been defeated!');
}
if (!isAlerting && userSettings.watchtowerAudioAlert && canPlayMoreAlerts()) {
startAlert();
}
break;
case 'Boss Defeated Idle Quest RPG':
if (userSettings.bossDesktopAlert) {
showNotification('IQRPG Boss Alert!', 'The boss has been defeated!');
}
if (userSettings.bossAudioAlert) {
playSound(userSettings.bossDefeatedSoundURL);
}
break;
case 'ALERT':
break;
default:
stopAlert();
currAutoAudioPlays = 0;
}
}
// Handle land mutations
function handleLandMutations(mutationRecords) {
mutationRecords.forEach(function(mutation) {
if (mutation.type == 'characterData') {
if (mutation.target.data == '00:00') {
if (userSettings.landAudioAlert) {
playSound(userSettings.landAlertSoundURL);
}
}
}
});
}
// Handle mastery mutations
function handleMasteryMutations(mutationRecords) {
mutationRecords.forEach(function(mutation) {
if (mutation.type == 'characterData') {
const level = parseInt(mutation.target.data);
if (!isNaN(level) && level % userSettings.masteryEveryXLevels == 0) {
if (userSettings.masteryAudioAlert) {
playSound(userSettings.masteryAlertSoundURL);
}
}
}
});
}
// Check for effects
function checkEffects() {
try {
const effects = $(".main-section__body > div > .flex.space-between > .green-text");
if (!effects || !effects.length) return;
effects.each(function(index) {
const effectLeft = $(effects[index])[0].innerHTML;
if (effectLeft == userSettings.effectAutoLeft) {
if (userSettings.effectAudioAlert) {
playSound(userSettings.effectAlertSoundURL);
}
}
});
} catch (error) {
// Silently fail if there's an error
}
}
// Check for bonus exp
function checkBonusExp() {
try {
const bonusExpSpan = $('.main-section__body> div > div > div > span.exp-text');
const hasBonus = bonusExpSpan != null && bonusExpSpan.length != 0;
if (hasBonus && !bonusExp) {
bonusExp = true;
if (userSettings.bonusExpAudioAlert) {
playSound(userSettings.bonusExpAlertSoundURL);
}
} else if (!hasBonus && bonusExp) {
bonusExp = false;
}
} catch (error) {
// Silently fail if there's an error
}
}
// Check if more alerts can be played
function canPlayMoreAlerts() {
return currAutoAudioPlays <= userSettings.autoMaxNumberOfAudioAlerts || userSettings.autoMaxNumberOfAudioAlerts == 0;
}
// Start alert sounds
function startAlert() {
if (isAlerting || !canPlayMoreAlerts()) return;
isAlerting = true;
currAutoAudioPlays++;
// Check if auto audio alert is enabled before playing sound
if (userSettings.autoAudioAlert) {
playSound(userSettings.autoAlertSoundURL);
}
const repeatInterval = userSettings.autoAlertRepeatInSeconds * 1000;
alertTimer = setInterval(() => {
if (canPlayMoreAlerts()) {
currAutoAudioPlays++;
// Check if auto audio alert is enabled before playing sound
if (userSettings.autoAudioAlert) {
playSound(userSettings.autoAlertSoundURL);
}
} else {
stopAlert();
}
}, repeatInterval);
}
// Stop alert sounds
function stopAlert() {
isAlerting = false;
if (alertTimer) {
clearInterval(alertTimer);
alertTimer = null;
}
}
// Show desktop notification
function showNotification(title, text) {
if (desktopNotificationOnCooldown) return;
try {
desktopNotificationOnCooldown = true;
setTimeout(() => { desktopNotificationOnCooldown = false; }, 7000);
if (!("Notification" in window)) {
return;
}
if (Notification.permission === "granted") {
const notification = new Notification(title, { body: text });
setupNotificationBehavior(notification);
} else if (Notification.permission !== "denied") {
Notification.requestPermission().then(function(permission) {
if (permission === "granted") {
const notification = new Notification(title, { body: text });
setupNotificationBehavior(notification);
}
});
}
} catch (error) {
// Silently fail if there's an error
}
}
// Setup notification behavior
function setupNotificationBehavior(notification) {
if (!notification) return;
notification.onclick = function() {
window.focus();
this.close();
};
setTimeout(() => {
if (notification) notification.close();
}, 7000);
}
// Play sound with volume control
function playSound(sound, volume = null) {
try {
if (!sound) return;
const audio = new Audio();
audio.src = sound;
audio.volume = volume !== null ? volume : userSettings.masterAudioLevel;
audio.play();
} catch (error) {
// Silently fail if there's an error
}
}
// Return public methods and properties
return {
init: init,
playSound: playSound,
showNotification: showNotification,
toggleSettingsPanel: toggleSettingsPanel,
stopAlert: stopAlert
};
})();
// Initialize the script when the document is ready
(function() {
// Wait for the page to be fully loaded
if (document.readyState === 'complete' || document.readyState === 'interactive') {
setTimeout(function() {
window.IQRPG_Plus.init();
}, 1000); // Slight delay to ensure everything is loaded
} else {
document.addEventListener('DOMContentLoaded', function() {
setTimeout(function() {
window.IQRPG_Plus.init();
}, 1000);
});
}
})();