You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

223 lines
5.5 KiB
Go

package game
import (
"testing"
"time"
"github.com/denisovdennis/autohero/internal/model"
)
func TestOrcWarriorBurstEveryThirdAttack(t *testing.T) {
enemy := &model.Enemy{
Type: model.EnemyOrc,
Attack: 12,
Speed: 1.0,
SpecialAbilities: []model.SpecialAbility{model.AbilityBurst},
}
// Attacks 1 and 2 should have multiplier 1.0
m1 := EnemyAttackDamageMultiplier(enemy)
if m1 != 1.0 {
t.Fatalf("attack 1: expected multiplier 1.0, got %.2f", m1)
}
m2 := EnemyAttackDamageMultiplier(enemy)
if m2 != 1.0 {
t.Fatalf("attack 2: expected multiplier 1.0, got %.2f", m2)
}
// Attack 3 should deal 1.5x
m3 := EnemyAttackDamageMultiplier(enemy)
if m3 != 1.5 {
t.Fatalf("attack 3: expected multiplier 1.5, got %.2f", m3)
}
// Attack 4 back to normal
m4 := EnemyAttackDamageMultiplier(enemy)
if m4 != 1.0 {
t.Fatalf("attack 4: expected multiplier 1.0, got %.2f", m4)
}
}
func TestLightningTitanChainLightning(t *testing.T) {
enemy := &model.Enemy{
Type: model.EnemyLightningTitan,
Attack: 30,
Speed: 1.5,
SpecialAbilities: []model.SpecialAbility{model.AbilityStun, model.AbilityChainLightning},
}
// Attacks 1-5 should be normal (no chain lightning)
for i := 1; i <= 5; i++ {
m := EnemyAttackDamageMultiplier(enemy)
if m != 1.0 {
t.Fatalf("attack %d: expected multiplier 1.0, got %.2f", i, m)
}
}
// Attack 6 triggers chain lightning (3x)
m6 := EnemyAttackDamageMultiplier(enemy)
if m6 != 3.0 {
t.Fatalf("attack 6: expected multiplier 3.0, got %.2f", m6)
}
}
func TestIceGuardianAppliesIceSlow(t *testing.T) {
hero := &model.Hero{
ID: 1, HP: 100, MaxHP: 100,
Attack: 10, Defense: 5, Speed: 1.0,
Strength: 5, Constitution: 5, Agility: 5,
}
enemy := &model.Enemy{
Type: model.EnemyIceGuardian,
Attack: 14,
Defense: 15,
Speed: 0.7,
SpecialAbilities: []model.SpecialAbility{model.AbilityIceSlow},
}
now := time.Now()
applied := false
for i := 0; i < 200; i++ {
hero.HP = hero.MaxHP
hero.Debuffs = nil
ProcessEnemyAttack(hero, enemy, now)
for _, d := range hero.Debuffs {
if d.Debuff.Type == model.DebuffIceSlow {
applied = true
if d.Debuff.Magnitude != 0.20 {
t.Fatalf("IceSlow magnitude should be 0.20, got %.2f", d.Debuff.Magnitude)
}
break
}
}
if applied {
break
}
}
if !applied {
t.Fatal("Ice Guardian never applied IceSlow debuff in 200 attacks")
}
}
func TestSkeletonKingSummonDamage(t *testing.T) {
hero := &model.Hero{
ID: 1, HP: 100, MaxHP: 100,
}
enemy := &model.Enemy{
Type: model.EnemySkeletonKing,
Attack: 18,
SpecialAbilities: []model.SpecialAbility{model.AbilityRegen, model.AbilitySummon},
}
start := time.Now()
// Before 15 seconds: no summon damage.
dmg := ProcessSummonDamage(hero, enemy, start, start, start.Add(10*time.Second))
if dmg != 0 {
t.Fatalf("expected no summon damage before 15s, got %d", dmg)
}
// At 15 seconds: summon damage should occur.
dmg = ProcessSummonDamage(hero, enemy, start, start.Add(14*time.Second), start.Add(16*time.Second))
if dmg == 0 {
t.Fatal("expected summon damage after 15s boundary crossed")
}
expectedDmg := max(1, enemy.Attack/4)
if dmg != expectedDmg {
t.Fatalf("expected summon damage %d, got %d", expectedDmg, dmg)
}
}
func TestLootGenerationOnEnemyDeath(t *testing.T) {
drops := model.GenerateLoot(model.EnemyWolf, 1.0)
if len(drops) == 0 {
t.Fatal("expected at least one loot drop (gold)")
}
hasGold := false
for _, d := range drops {
if d.ItemType == "gold" {
hasGold = true
if d.GoldAmount <= 0 {
t.Fatal("gold drop should have positive amount")
}
}
}
if !hasGold {
t.Fatal("expected gold drop from GenerateLoot")
}
}
func TestLuckMultiplierWithBuff(t *testing.T) {
now := time.Now()
hero := &model.Hero{
Buffs: []model.ActiveBuff{{
Buff: model.DefaultBuffs[model.BuffLuck],
AppliedAt: now.Add(-time.Second),
ExpiresAt: now.Add(10 * time.Second),
}},
}
mult := LuckMultiplier(hero, now)
if mult != 2.5 {
t.Fatalf("expected luck multiplier 2.5, got %.1f", mult)
}
}
func TestLuckMultiplierWithoutBuff(t *testing.T) {
hero := &model.Hero{}
mult := LuckMultiplier(hero, time.Now())
if mult != 1.0 {
t.Fatalf("expected luck multiplier 1.0 without buff, got %.1f", mult)
}
}
func TestProcessDebuffDamageAppliesPoison(t *testing.T) {
now := time.Now()
hero := &model.Hero{
HP: 100, MaxHP: 100,
Debuffs: []model.ActiveDebuff{{
Debuff: model.DefaultDebuffs[model.DebuffPoison],
AppliedAt: now.Add(-time.Second),
ExpiresAt: now.Add(4 * time.Second),
}},
}
dmg := ProcessDebuffDamage(hero, time.Second, now)
if dmg == 0 {
t.Fatal("expected poison to deal damage over time")
}
if hero.HP >= 100 {
t.Fatal("expected hero HP to decrease from poison")
}
}
func TestDodgeAbilityCanAvoidDamage(t *testing.T) {
now := time.Now()
hero := &model.Hero{
ID: 1, HP: 100, MaxHP: 100,
Attack: 50, Defense: 0, Speed: 1.0,
Strength: 10, Agility: 5,
}
enemy := &model.Enemy{
Type: model.EnemySkeletonArcher,
MaxHP: 1000,
HP: 1000,
Attack: 10,
Defense: 0,
SpecialAbilities: []model.SpecialAbility{model.AbilityDodge},
}
dodged := false
for i := 0; i < 200; i++ {
enemy.HP = enemy.MaxHP
evt := ProcessAttack(hero, enemy, now)
if evt.Damage == 0 {
dodged = true
break
}
}
if !dodged {
t.Fatal("expected at least one dodge in 200 hero attacks against Skeleton Archer")
}
}