@ -2574,6 +2574,32 @@
pointer-events: none;
pointer-events: none;
text-decoration: line-through;
text-decoration: line-through;
}
}
.monster-entry.attacking {
border-color: #ff6b35;
box-shadow: 0 0 15px rgba(255, 107, 53, 0.6);
background: rgba(255, 107, 53, 0.15);
}
/* Rubber band attack animation for monster icon */
@keyframes monsterAttack {
0% {
transform: translateX(0);
}
20% {
transform: translateX(20px) scale(0.9);
}
50% {
transform: translateX(-30px) scale(1.15);
}
70% {
transform: translateX(-5px) scale(1.05);
}
100% {
transform: translateX(0) scale(1);
}
}
.monster-entry.attacking .monster-entry-icon {
animation: monsterAttack 0.5s ease-out;
}
.monster-entry-header {
.monster-entry-header {
display: flex;
display: flex;
align-items: center;
align-items: center;
@ -10842,7 +10868,8 @@
let loginMusicStarted = false;
let loginMusicStarted = false;
function startLoginMusic() {
function startLoginMusic() {
if (!loginMusicStarted & & !gameMusic.muted) {
const alreadyLoggedIn = localStorage.getItem('accessToken') || sessionStorage.getItem('guestMode');
if (!loginMusicStarted & & !gameMusic.muted & & !alreadyLoggedIn) {
loginMusicStarted = true;
loginMusicStarted = true;
playMusic('login');
playMusic('login');
}
}
@ -11016,8 +11043,10 @@
// Try to autostart login music immediately (works if user has interacted before)
// Try to autostart login music immediately (works if user has interacted before)
// Falls back to first interaction if autoplay is blocked
// Falls back to first interaction if autoplay is blocked
// Skip if user is already logged in (will go straight to game music)
setTimeout(() => {
setTimeout(() => {
if (!loginMusicStarted & & !gameMusic.muted) {
const alreadyLoggedIn = localStorage.getItem('accessToken') || sessionStorage.getItem('guestMode');
if (!loginMusicStarted & & !gameMusic.muted & & !alreadyLoggedIn) {
const loginAudio = gameMusic.login;
const loginAudio = gameMusic.login;
loginAudio.play().then(() => {
loginAudio.play().then(() => {
loginMusicStarted = true;
loginMusicStarted = true;
@ -13526,6 +13555,31 @@
}
}
}
}
// Scroll to the attacking monster and trigger rubber band animation
function animateMonsterAttack(monsterIndex) {
try {
const container = document.getElementById('monsterList');
if (!container) return;
const entries = container.querySelectorAll('.monster-entry');
const entry = entries[monsterIndex];
if (!entry) return;
// Scroll the monster into view smoothly
entry.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
// Force restart the rubber band animation on the icon
const icon = entry.querySelector('.monster-entry-icon');
if (icon) {
icon.style.animation = 'none';
icon.offsetHeight; // Trigger reflow
icon.style.animation = 'monsterAttack 0.5s ease-out';
}
} catch (e) {
console.error('Animation error:', e);
}
}
// Render the monster list in combat UI
// Render the monster list in combat UI
function renderMonsterList() {
function renderMonsterList() {
const container = document.getElementById('monsterList');
const container = document.getElementById('monsterList');
@ -13546,6 +13600,10 @@
if (combatState.targetingMode & & monster.hp > 0) {
if (combatState.targetingMode & & monster.hp > 0) {
entry.classList.add('targeting-selectable');
entry.classList.add('targeting-selectable');
}
}
// Highlight the currently attacking monster
if (combatState.turn === 'monster' & & index === combatState.currentMonsterTurn & & monster.hp > 0) {
entry.classList.add('attacking');
}
const hpPct = Math.max(0, (monster.hp / monster.maxHp) * 100);
const hpPct = Math.max(0, (monster.hp / monster.maxHp) * 100);
const mpPct = Math.max(0, (monster.mp / monster.maxMp) * 100);
const mpPct = Math.max(0, (monster.mp / monster.maxMp) * 100);
@ -14201,6 +14259,9 @@
combatState.currentMonsterTurn = monsterIndex;
combatState.currentMonsterTurn = monsterIndex;
updateCombatUI();
updateCombatUI();
// Scroll to and animate the attacking monster (after DOM update)
setTimeout(() => animateMonsterAttack(monsterIndex), 50);
// Decrement monster buff durations at start of its turn
// Decrement monster buff durations at start of its turn
if (monster.buffs) {
if (monster.buffs) {
if (monster.buffs.defense & & monster.buffs.defense.turnsLeft > 0) {
if (monster.buffs.defense & & monster.buffs.defense.turnsLeft > 0) {