diff --git a/changelog.js b/changelog.js
index 22264d6..b3c6bf5 100644
--- a/changelog.js
+++ b/changelog.js
@@ -1,7 +1,16 @@
// Version tracking
-export const APP_VERSION = '1.0.23';
+export const APP_VERSION = '1.0.24';
export const CHANGELOG = {
+ '1.0.24': {
+ date: '2026-02-02',
+ changes: {
+ features: [
+ 'Add Discover More link to info dialog',
+ 'Add auto-open-nominations',
+ ],
+ },
+ },
'1.0.23': {
date: '2026-02-02',
changes: {
diff --git a/index.html b/index.html
index acde052..de37838 100644
--- a/index.html
+++ b/index.html
@@ -133,6 +133,16 @@
>00
+
+ Nominations open in 0s
+
+
@@ -672,6 +715,15 @@ About Tower Timer
/>
+
+
+ Discover more on the main Arcane Scripts site
+
+
This timer is an open source project and is not affiliated with
{
// Initialize audio
endSound = new Audio(`sounds/end-of-day/${endOfDaySound}`);
wakeUpSound = new Audio(`sounds/wake-up/${wakeUpSoundFile}`);
+ nominationsOpenSound = new Audio(
+ `sounds/nominations-open/${nominationsOpenSoundFile}`
+ );
// Add connectivity listeners
connectivityUtils.addStatusListener(
@@ -897,8 +906,12 @@ document.addEventListener('DOMContentLoaded', async () => {
.forEach((btn) => btn.classList.remove('playing'));
}
- // Create and play the new preview
- previewSound = new Audio(`sounds/${type}/${soundFile}`);
+ // Create and play the new preview (nominations-open uses its own folder)
+ const previewPath =
+ type === 'nominations-open'
+ ? `sounds/nominations-open/${soundFile}`
+ : `sounds/${type}/${soundFile}`;
+ previewSound = new Audio(previewPath);
button.classList.add('playing');
previewSound.addEventListener(
@@ -944,6 +957,54 @@ document.addEventListener('DOMContentLoaded', async () => {
saveSettings();
});
+ document
+ .getElementById('nominationsOpenSound')
+ .addEventListener('change', (e) => {
+ if (previewSound) {
+ previewSound.pause();
+ previewSound.currentTime = 0;
+ document
+ .querySelectorAll('.preview-sound')
+ .forEach((btn) => btn.classList.remove('playing'));
+ }
+ nominationsOpenSoundFile = e.target.value;
+ nominationsOpenSound = new Audio(
+ `sounds/nominations-open/${nominationsOpenSoundFile}`
+ );
+ saveSettings();
+ });
+
+ document
+ .getElementById('autoOpenNominations')
+ .addEventListener('change', (e) => {
+ autoOpenNominations = e.target.checked;
+ document.getElementById('autoOpenNominationsDelay').disabled =
+ !autoOpenNominations;
+ const nominationsOpenSelect = document.getElementById(
+ 'nominationsOpenSound'
+ );
+ const nominationsOpenLabelEl = document.querySelector(
+ 'label:has(#nominationsOpenSound)'
+ );
+ if (nominationsOpenSelect) {
+ nominationsOpenSelect.disabled = !autoOpenNominations;
+ }
+ if (nominationsOpenLabelEl) {
+ nominationsOpenLabelEl.classList.toggle(
+ 'inactive',
+ !playSoundEffects || !autoOpenNominations
+ );
+ }
+ saveSettings();
+ });
+
+ document
+ .getElementById('autoOpenNominationsDelay')
+ .addEventListener('change', (e) => {
+ autoOpenNominationsDelay = parseInt(e.target.value, 10);
+ saveSettings();
+ });
+
// Initialize settings and update display
loadSettings();
updateClocktowerPresets();
@@ -1013,6 +1074,13 @@ function loadSettings() {
settings.youtubePlaylistUrl || DEFAULT_YOUTUBE_PLAYLIST;
endOfDaySound = settings.endOfDaySound || 'cathedral-bell-v2.mp3';
wakeUpSoundFile = settings.wakeUpSoundFile || 'chisel-bell-01-loud-v2.mp3';
+ nominationsOpenSoundFile =
+ settings.nominationsOpenSoundFile || 'nominations-open-laura.mp3';
+ autoOpenNominations = settings.autoOpenNominations || false;
+ autoOpenNominationsDelay =
+ settings.autoOpenNominationsDelay !== undefined
+ ? settings.autoOpenNominationsDelay
+ : 60;
acceptedPortraitWarning = settings.acceptedPortraitWarning || false;
keyboardShortcuts = settings.keyboardShortcuts || {
...DEFAULT_KEYBOARD_SHORTCUTS,
@@ -1041,6 +1109,9 @@ function loadSettings() {
currentDay = 1;
endSound = new Audio(`sounds/end-of-day/${endOfDaySound}`);
wakeUpSound = new Audio(`sounds/wake-up/${wakeUpSoundFile}`);
+ nominationsOpenSound = new Audio(
+ `sounds/nominations-open/${nominationsOpenSoundFile}`
+ );
}
// Always update UI to reflect settings
@@ -1063,6 +1134,33 @@ function loadSettings() {
).textContent = `${soundEffectsVolume}%`;
document.getElementById('endOfDaySound').value = endOfDaySound;
document.getElementById('wakeUpSound').value = wakeUpSoundFile;
+ document.getElementById('nominationsOpenSound').value =
+ nominationsOpenSoundFile;
+ document.getElementById('autoOpenNominations').checked = autoOpenNominations;
+ document.getElementById('autoOpenNominationsDelay').value =
+ autoOpenNominationsDelay;
+ document.getElementById('autoOpenNominationsDelay').disabled =
+ !autoOpenNominations;
+
+ const nominationsOpenSoundSelect = document.getElementById(
+ 'nominationsOpenSound'
+ );
+ const nominationsOpenLabel = document.querySelector(
+ 'label:has(#nominationsOpenSound)'
+ );
+ if (nominationsOpenSoundSelect) {
+ nominationsOpenSoundSelect.disabled = !autoOpenNominations;
+ }
+ if (nominationsOpenLabel) {
+ nominationsOpenLabel.classList.toggle(
+ 'inactive',
+ !playSoundEffects || !autoOpenNominations
+ );
+ }
+
+ nominationsOpenSound = new Audio(
+ `sounds/nominations-open/${nominationsOpenSoundFile}`
+ );
// Enable/disable volume controls based on their respective settings
const musicVolumeInput = document.getElementById('musicVolume');
@@ -1202,6 +1300,9 @@ function saveSettings() {
lastSeenVersion: APP_VERSION,
endOfDaySound,
wakeUpSoundFile,
+ nominationsOpenSoundFile,
+ autoOpenNominations,
+ autoOpenNominationsDelay,
acceptedPortraitWarning,
keyboardShortcuts,
};
@@ -1421,6 +1522,60 @@ function playEndSound() {
);
}
+// Play nominations open sound
+function playNominationsOpenSound() {
+ if (!playSoundEffects || !nominationsOpenSound) return;
+ nominationsOpenSound.currentTime = 0;
+ nominationsOpenSound.volume = soundEffectsVolume / 100;
+ nominationsOpenSound.play().catch((error) => {
+ console.log('Error playing nominations open sound:', error);
+ });
+}
+
+// Clear the nominations countdown (interval and UI)
+function clearNominationsCountdown() {
+ if (autoOpenNominationsInterval) {
+ clearInterval(autoOpenNominationsInterval);
+ autoOpenNominationsInterval = null;
+ }
+ const el = document.getElementById('nominationsCountdown');
+ if (el) {
+ el.hidden = true;
+ }
+}
+
+// Start the nominations countdown display and schedule the sound
+function startNominationsCountdown() {
+ clearNominationsCountdown();
+ if (!autoOpenNominations || autoOpenNominationsDelay <= 0) return;
+
+ const countdownEl = document.getElementById('nominationsCountdown');
+ const secondsEl = document.getElementById('nominationsCountdownSeconds');
+ if (!countdownEl || !secondsEl) return;
+
+ nominationsCountdownRemaining = autoOpenNominationsDelay;
+ secondsEl.textContent = nominationsCountdownRemaining;
+ countdownEl.hidden = false;
+ countdownEl.setAttribute(
+ 'aria-label',
+ `Nominations open in ${nominationsCountdownRemaining} seconds`
+ );
+
+ autoOpenNominationsInterval = setInterval(() => {
+ nominationsCountdownRemaining--;
+ secondsEl.textContent = nominationsCountdownRemaining;
+ countdownEl.setAttribute(
+ 'aria-label',
+ `Nominations open in ${nominationsCountdownRemaining} seconds`
+ );
+
+ if (nominationsCountdownRemaining <= 0) {
+ clearNominationsCountdown();
+ playNominationsOpenSound();
+ }
+ }, 1000);
+}
+
// Create beep sound (fallback if mp3 fails to load)
function createBeep() {
if (!playSoundEffects) return;
@@ -1520,6 +1675,10 @@ function accelerateTime() {
updateDayDisplay('dusk');
}
updateDisplay(); // Make sure to update display one final time
+
+ if (autoOpenNominations && autoOpenNominationsDelay > 0) {
+ startNominationsCountdown();
+ }
}
}, currentInterval);
@@ -1572,6 +1731,7 @@ function resetTimer() {
clearTimeout(wakeUpTimeout);
wakeUpTimeout = null;
}
+ clearNominationsCountdown();
// Reset to the full day countdown time (use current day's preset, which includes pace)
const day = currentDay ?? 1;
@@ -1679,6 +1839,7 @@ function playWakeUpSound() {
if (wakeUpTimeout) {
clearTimeout(wakeUpTimeout);
}
+ clearNominationsCountdown();
if (playSoundEffects) {
// Stop music if playing and not set to play at night
@@ -1803,6 +1964,10 @@ function startCountdown() {
updateDayDisplay('dusk');
}
updateDisplay();
+
+ if (autoOpenNominations && autoOpenNominationsDelay > 0) {
+ startNominationsCountdown();
+ }
}
}, normalInterval);
}
@@ -1810,6 +1975,7 @@ function startCountdown() {
function startNewGame() {
// Reset timer state
clearInterval(timerId);
+ clearNominationsCountdown();
timeLeft = 0;
isRunning = false;
currentInterval = normalInterval;
@@ -2333,6 +2499,10 @@ function updateSoundEffects() {
element: document.querySelector('label:has(#wakeUpSound)'),
type: 'label',
},
+ {
+ element: document.querySelector('label:has(#nominationsOpenSound)'),
+ type: 'label',
+ },
{
element: document.querySelector('label:has(#soundEffectsVolume)'),
type: 'label',
@@ -2340,17 +2510,31 @@ function updateSoundEffects() {
];
// Apply inactive state to all dependent elements
+ const nominationsOpenLabel = document.querySelector(
+ 'label:has(#nominationsOpenSound)'
+ );
+ const nominationsOpenSelect = document.getElementById('nominationsOpenSound');
+
soundDependentElements.forEach(({ element, type }) => {
if (element) {
- element.classList.toggle('inactive', !playSoundEffects);
+ const isNominationsOpen = element === nominationsOpenLabel;
+ const inactive = isNominationsOpen
+ ? !playSoundEffects || !autoOpenNominations
+ : !playSoundEffects;
+ element.classList.toggle('inactive', inactive);
element.setAttribute(
'data-inactive-message',
- 'Enable "Play Sound Effects" first'
+ isNominationsOpen && !autoOpenNominations
+ ? 'Enable "Automatically open nominations" in Game settings first'
+ : 'Enable "Play Sound Effects" first'
);
if (type === 'label') {
const input = element.querySelector('select, input');
if (input) {
- input.setAttribute('aria-hidden', !playSoundEffects);
+ input.setAttribute('aria-hidden', inactive);
+ if (input === nominationsOpenSelect) {
+ input.disabled = !autoOpenNominations;
+ }
}
}
}
diff --git a/sounds/nominations-open/nominations-open-laura.mp3 b/sounds/nominations-open/nominations-open-laura.mp3
new file mode 100644
index 0000000..42cf03d
Binary files /dev/null and b/sounds/nominations-open/nominations-open-laura.mp3 differ
diff --git a/sounds/nominations-open/nominations-open-russell.mp3 b/sounds/nominations-open/nominations-open-russell.mp3
new file mode 100644
index 0000000..26b1793
Binary files /dev/null and b/sounds/nominations-open/nominations-open-russell.mp3 differ
diff --git a/sounds/nominations-open/nominations-open-serafina.mp3 b/sounds/nominations-open/nominations-open-serafina.mp3
new file mode 100644
index 0000000..f8bfdd6
Binary files /dev/null and b/sounds/nominations-open/nominations-open-serafina.mp3 differ
diff --git a/styles.css b/styles.css
index 155e43d..1858e8b 100644
--- a/styles.css
+++ b/styles.css
@@ -215,6 +215,10 @@ body[data-pace='blitz'] .disclaimer a {
color: #f44336;
}
+body[data-pace='blitz'] .main-site-link a {
+ color: #f44336;
+}
+
body[data-pace='blitz'] .portrait-warning-dialog h2 {
color: #f44336;
}
@@ -308,6 +312,21 @@ h1 {
margin-bottom: -0.5em;
}
+.nominations-countdown {
+ font-size: min(4vw, 1.5rem);
+ color: rgba(255, 255, 255, 0.9);
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
+ margin-top: 0.5rem;
+ padding: 0.25rem 0.75rem;
+ background: rgba(76, 175, 80, 0.25);
+ border-radius: 6px;
+ line-height: 1.2;
+}
+
+.nominations-countdown[hidden] {
+ display: none;
+}
+
@keyframes pulse-text {
0% {
opacity: 1;
@@ -790,6 +809,12 @@ h1 {
font-weight: normal;
}
+.setting-group label.auto-nominations-row .auto-nominations-label {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
body[data-pace='blitz'] .info-value {
color: #f44336;
}
@@ -1388,6 +1413,20 @@ body:has(.clocktower-settings.visible) #regularTimerControls {
text-align: center;
}
+.main-site-link {
+ margin: 0;
+ text-align: center;
+}
+
+.main-site-link a {
+ color: #4caf50;
+ text-decoration: none;
+}
+
+.main-site-link a:hover {
+ text-decoration: underline;
+}
+
.kofi-container img {
height: 36px;
width: auto;