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
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")
|
|
}
|
|
}
|