diff --git a/database.js b/database.js index d23eaf2..fd7e668 100644 --- a/database.js +++ b/database.js @@ -1115,11 +1115,7 @@ class HikeMapDB { // ===================== seedDefaultSkills() { - // Check if skills already exist - const existing = this.getSkill('basic_attack'); - if (existing) return; - - console.log('Seeding default skills...'); + console.log('Checking/seeding default skills...'); const defaultSkills = [ { @@ -1205,10 +1201,28 @@ class HikeMapDB { statusEffect: { type: 'poison', damage: 5, duration: 3 }, playerUsable: false, monsterUsable: true + }, + { + id: 'whirlwind', + name: 'Whirlwind', + description: 'A spinning attack that hits all enemies', + type: 'damage', + mpCost: 12, + basePower: 75, + accuracy: 85, + hitCount: 1, + target: 'all_enemies', + statusEffect: null, + playerUsable: true, + monsterUsable: false } ]; for (const skill of defaultSkills) { + // Only seed if skill doesn't exist + const existing = this.getSkill(skill.id); + if (existing) continue; + this.createSkill(skill); console.log(` Seeded skill: ${skill.name}`); } @@ -1222,20 +1236,28 @@ class HikeMapDB { ]; for (const name of trailRunnerSkillNames) { - this.createClassSkillName(name); - console.log(` Seeded class skill name: ${name.customName} for ${name.classId}`); + try { + this.createClassSkillName(name); + console.log(` Seeded class skill name: ${name.customName} for ${name.classId}`); + } catch (err) { + // Skip if already exists + } } // Assign poison skill to Moop monster const moop = this.getMonsterType('moop'); if (moop) { - this.createMonsterSkill({ - monsterTypeId: 'moop', - skillId: 'poison', - weight: 30, - minLevel: 1 - }); - console.log(' Assigned poison skill to Moop'); + try { + this.createMonsterSkill({ + monsterTypeId: 'moop', + skillId: 'poison', + weight: 30, + minLevel: 1 + }); + console.log(' Assigned poison skill to Moop'); + } catch (err) { + // Skip if already exists + } } console.log('Default skills seeded successfully'); diff --git a/index.html b/index.html index d3d5d2f..d9ffa49 100644 --- a/index.html +++ b/index.html @@ -3561,7 +3561,7 @@ mpPerLevel: 10, atkPerLevel: 3, defPerLevel: 2, - skills: ['basic_attack', 'brand_new_hokas', 'runners_high', 'shin_kick'] + skills: ['basic_attack', 'brand_new_hokas', 'runners_high', 'shin_kick', 'whirlwind'] }, 'gym_bro': { name: 'Gym Bro', @@ -3677,6 +3677,16 @@ type: 'admin_clear', adminOnly: true, description: 'Instantly banish all enemies (Admin only)' + }, + 'whirlwind': { + name: 'Whirlwind', + icon: '🌀', + mpCost: 12, + levelReq: 1, + type: 'damage', + target: 'all_enemies', + calculate: (atk) => Math.floor(atk * 0.75), + description: 'Spinning attack that hits all enemies for 75% ATK' } }; @@ -11295,22 +11305,12 @@ }, 1000); return; } else if (skill.type === 'damage') { - // Calculate hit chance - const skillAccuracy = dbSkill ? dbSkill.accuracy : 95; - const hitChance = calculateHitChance( - combatState.player.accuracy, - target.dodge, - skillAccuracy - ); - - // Roll for hit - if (!rollHit(hitChance)) { - addCombatLog(`❌ ${displayName} missed ${target.data.name}! (${hitChance}% chance)`, 'miss'); - endPlayerTurn(); - return; - } + // Determine targets based on skill.target + const skillTarget = dbSkill ? dbSkill.target : (skill.target || 'enemy'); + const livingMonsters = combatState.monsters.filter(m => m.hp > 0); + const targets = skillTarget === 'all_enemies' ? livingMonsters : [target]; - // Calculate damage - support both old calculate() and new basePower + // Calculate base damage - support both old calculate() and new basePower let rawDamage; if (hardcodedSkill && hardcodedSkill.calculate) { rawDamage = hardcodedSkill.calculate(combatState.player.atk); @@ -11320,32 +11320,79 @@ rawDamage = combatState.player.atk; } - // Handle multi-hit skills const hitCount = skill.hitCount || skill.hits || 1; - let totalDamage = 0; + const skillAccuracy = dbSkill ? dbSkill.accuracy : 95; + let grandTotalDamage = 0; + let monstersHit = 0; + let monstersKilled = 0; + + // Apply damage to each target + for (const currentTarget of targets) { + // Calculate hit chance for this target + const hitChance = calculateHitChance( + combatState.player.accuracy, + currentTarget.dodge, + skillAccuracy + ); - // Calculate effective defense (with buff if active) - let effectiveMonsterDef = target.def; - if (target.buffs && target.buffs.defense && target.buffs.defense.turnsLeft > 0) { - const buffPercent = target.buffs.defense.percent || 50; - effectiveMonsterDef = Math.floor(target.def * (1 + buffPercent / 100)); - } + // Roll for hit + if (!rollHit(hitChance)) { + if (targets.length === 1) { + addCombatLog(`❌ ${displayName} missed ${currentTarget.data.name}! (${hitChance}% chance)`, 'miss'); + } + continue; // Miss this target, continue to next + } - for (let hit = 0; hit < hitCount; hit++) { - const damage = Math.max(1, rawDamage - effectiveMonsterDef); - totalDamage += damage; - target.hp -= damage; + monstersHit++; + let totalDamage = 0; + + // Calculate effective defense (with buff if active) + let effectiveMonsterDef = currentTarget.def; + if (currentTarget.buffs && currentTarget.buffs.defense && currentTarget.buffs.defense.turnsLeft > 0) { + const buffPercent = currentTarget.buffs.defense.percent || 50; + effectiveMonsterDef = Math.floor(currentTarget.def * (1 + buffPercent / 100)); + } + + for (let hit = 0; hit < hitCount; hit++) { + const damage = Math.max(1, rawDamage - effectiveMonsterDef); + totalDamage += damage; + currentTarget.hp -= damage; + } + + grandTotalDamage += totalDamage; + + // Log for single-target skills + if (targets.length === 1) { + if (hitCount > 1) { + addCombatLog(`✨ ${displayName} hits ${currentTarget.data.name} ${hitCount} times for ${totalDamage} total damage!`, 'damage'); + } else { + addCombatLog(`⚔️ ${displayName} hits ${currentTarget.data.name} for ${totalDamage} damage!`, 'damage'); + } + } + + // Check if this monster died + if (currentTarget.hp <= 0) { + monstersKilled++; + if (targets.length === 1) { + addCombatLog(`💀 ${currentTarget.data.name} was defeated!`, 'victory'); + } + } } - if (hitCount > 1) { - addCombatLog(`✨ ${displayName} hits ${target.data.name} ${hitCount} times for ${totalDamage} total damage!`, 'damage'); - } else { - addCombatLog(`⚔️ ${displayName} hits ${target.data.name} for ${totalDamage} damage!`, 'damage'); + // Log for multi-target skills + if (targets.length > 1) { + if (monstersHit === 0) { + addCombatLog(`❌ ${displayName} missed all enemies!`, 'miss'); + } else { + addCombatLog(`🌟 ${displayName} hits ${monstersHit} enemies for ${grandTotalDamage} total damage!`, 'damage'); + if (monstersKilled > 0) { + addCombatLog(`💀 ${monstersKilled} enemy${monstersKilled > 1 ? 'ies' : ''} defeated!`, 'victory'); + } + } } - // Check if this monster died + // Auto-retarget if current target died if (target.hp <= 0) { - addCombatLog(`💀 ${target.data.name} was defeated!`, 'victory'); autoRetarget(); } } else if (skill.type === 'heal') {