@ -3561,7 +3561,7 @@
mpPerLevel: 10,
mpPerLevel: 10,
atkPerLevel: 3,
atkPerLevel: 3,
defPerLevel: 2,
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': {
'gym_bro': {
name: 'Gym Bro',
name: 'Gym Bro',
@ -3677,6 +3677,16 @@
type: 'admin_clear',
type: 'admin_clear',
adminOnly: true,
adminOnly: true,
description: 'Instantly banish all enemies (Admin only)'
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);
}, 1000);
return;
return;
} else if (skill.type === 'damage') {
} 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;
let rawDamage;
if (hardcodedSkill & & hardcodedSkill.calculate) {
if (hardcodedSkill & & hardcodedSkill.calculate) {
rawDamage = hardcodedSkill.calculate(combatState.player.atk);
rawDamage = hardcodedSkill.calculate(combatState.player.atk);
@ -11320,32 +11320,79 @@
rawDamage = combatState.player.atk;
rawDamage = combatState.player.atk;
}
}
// Handle multi-hit skills
const hitCount = skill.hitCount || skill.hits || 1;
const hitCount = skill.hitCount || skill.hits || 1;
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
);
// 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
}
monstersHit++;
let totalDamage = 0;
let totalDamage = 0;
// Calculate effective defense (with buff if active)
// 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));
let effectiveMonsterDef = curren tT arget.def;
if (curren tT arget.buffs & & curren tT arget.buffs.defense & & curren tT arget.buffs.defense.turnsLeft > 0) {
const buffPercent = curren tT arget.buffs.defense.percent || 50;
effectiveMonsterDef = Math.floor(curren tT arget.def * (1 + buffPercent / 100));
}
}
for (let hit = 0; hit < hitCount ; hit + + ) {
for (let hit = 0; hit < hitCount ; hit + + ) {
const damage = Math.max(1, rawDamage - effectiveMonsterDef);
const damage = Math.max(1, rawDamage - effectiveMonsterDef);
totalDamage += damage;
totalDamage += damage;
target.hp -= damage;
curren tT arget.hp -= damage;
}
}
grandTotalDamage += totalDamage;
// Log for single-target skills
if (targets.length === 1) {
if (hitCount > 1) {
if (hitCount > 1) {
addCombatLog(`✨ ${displayName} hits ${target.data.name} ${hitCount} times for ${totalDamage} total damage!`, 'damage');
addCombatLog(`✨ ${displayName} hits ${curren tT arget.data.name} ${hitCount} times for ${totalDamage} total damage!`, 'damage');
} else {
} else {
addCombatLog(`⚔️ ${displayName} hits ${target.data.name} for ${totalDamage} damage!`, 'damage');
addCombatLog(`⚔️ ${displayName} hits ${currentTarget.data.name} for ${totalDamage} damage!`, 'damage');
}
}
}
// Check if this monster died
// Check if this monster died
if (currentTarget.hp < = 0) {
monstersKilled++;
if (targets.length === 1) {
addCombatLog(`💀 ${currentTarget.data.name} was defeated!`, 'victory');
}
}
}
// 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');
}
}
}
// Auto-retarget if current target died
if (target.hp < = 0) {
if (target.hp < = 0) {
addCombatLog(`💀 ${target.data.name} was defeated!`, 'victory');
autoRetarget();
autoRetarget();
}
}
} else if (skill.type === 'heal') {
} else if (skill.type === 'heal') {