|
|
|
@ -9,6 +9,7 @@ import (
|
|
|
|
"sync"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hero_actions "github.com/denisovdennis/autohero/internal/hero"
|
|
|
|
"github.com/denisovdennis/autohero/internal/model"
|
|
|
|
"github.com/denisovdennis/autohero/internal/model"
|
|
|
|
"github.com/denisovdennis/autohero/internal/storage"
|
|
|
|
"github.com/denisovdennis/autohero/internal/storage"
|
|
|
|
"github.com/denisovdennis/autohero/internal/tuning"
|
|
|
|
"github.com/denisovdennis/autohero/internal/tuning"
|
|
|
|
@ -614,11 +615,6 @@ func (e *Engine) handleUsePotion(msg IncomingMessage) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
hero := hm.Hero
|
|
|
|
hero := hm.Hero
|
|
|
|
|
|
|
|
|
|
|
|
// Validate: hero is in combat, has potions, is alive.
|
|
|
|
|
|
|
|
if hm.State != model.StateFighting {
|
|
|
|
|
|
|
|
e.sendError(msg.HeroID, "not_fighting", "hero is not in combat")
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if hero.Potions <= 0 {
|
|
|
|
if hero.Potions <= 0 {
|
|
|
|
e.sendError(msg.HeroID, "no_potions", "no potions available")
|
|
|
|
e.sendError(msg.HeroID, "no_potions", "no potions available")
|
|
|
|
return
|
|
|
|
return
|
|
|
|
@ -628,15 +624,7 @@ func (e *Engine) handleUsePotion(msg IncomingMessage) {
|
|
|
|
return
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
hero.Potions--
|
|
|
|
healAmount := hero_actions.UsePotionOnHero(hero, true)
|
|
|
|
healAmount := int(float64(hero.MaxHP) * tuning.Get().PotionHealPercent)
|
|
|
|
|
|
|
|
if healAmount < 1 {
|
|
|
|
|
|
|
|
healAmount = 1
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
hero.HP += healAmount
|
|
|
|
|
|
|
|
if hero.HP > hero.MaxHP {
|
|
|
|
|
|
|
|
hero.HP = hero.MaxHP
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hm.SyncToHero()
|
|
|
|
hm.SyncToHero()
|
|
|
|
|
|
|
|
|
|
|
|
@ -653,7 +641,7 @@ func (e *Engine) handleUsePotion(msg IncomingMessage) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if e.adventureLog != nil {
|
|
|
|
if e.adventureLog != nil && healAmount > 0 {
|
|
|
|
e.adventureLog(msg.HeroID, model.AdventureLogLine{
|
|
|
|
e.adventureLog(msg.HeroID, model.AdventureLogLine{
|
|
|
|
Event: &model.AdventureLogEvent{
|
|
|
|
Event: &model.AdventureLogEvent{
|
|
|
|
Code: model.LogPhraseUsedHealingPotion,
|
|
|
|
Code: model.LogPhraseUsedHealingPotion,
|
|
|
|
@ -676,8 +664,6 @@ func (e *Engine) handleUsePotion(msg IncomingMessage) {
|
|
|
|
HeroHP: hero.HP,
|
|
|
|
HeroHP: hero.HP,
|
|
|
|
EnemyHP: enemyHP,
|
|
|
|
EnemyHP: enemyHP,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
hero.EnsureGearMap()
|
|
|
|
|
|
|
|
hero.RefreshDerivedCombatStats(time.Now())
|
|
|
|
|
|
|
|
e.sender.SendToHero(msg.HeroID, "hero_state", hero)
|
|
|
|
e.sender.SendToHero(msg.HeroID, "hero_state", hero)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -1619,6 +1605,8 @@ func (e *Engine) processCombatTickLocked(now time.Time) {
|
|
|
|
dotDmg := ProcessDebuffDamage(cs.Hero, tickDur, now)
|
|
|
|
dotDmg := ProcessDebuffDamage(cs.Hero, tickDur, now)
|
|
|
|
regenHealed := ProcessEnemyRegen(&cs.Enemy, tickDur, &cs.EnemyRegenRemainder)
|
|
|
|
regenHealed := ProcessEnemyRegen(&cs.Enemy, tickDur, &cs.EnemyRegenRemainder)
|
|
|
|
summonDmg := ProcessSummonDamage(cs.Hero, &cs.Enemy, cs.StartedAt, cs.LastTickAt, now)
|
|
|
|
summonDmg := ProcessSummonDamage(cs.Hero, &cs.Enemy, cs.StartedAt, cs.LastTickAt, now)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cs.LastTickAt = now
|
|
|
|
cs.LastTickAt = now
|
|
|
|
if e.sender != nil {
|
|
|
|
if e.sender != nil {
|
|
|
|
if dotDmg > 0 {
|
|
|
|
if dotDmg > 0 {
|
|
|
|
@ -1643,6 +1631,7 @@ func (e *Engine) processCombatTickLocked(now time.Time) {
|
|
|
|
EnemyHP: cs.Enemy.HP,
|
|
|
|
EnemyHP: cs.Enemy.HP,
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if CheckDeath(cs.Hero, now) {
|
|
|
|
if CheckDeath(cs.Hero, now) {
|
|
|
|
@ -1667,7 +1656,6 @@ func (e *Engine) processCombatTickLocked(now time.Time) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Process all attacks that are due.
|
|
|
|
|
|
|
|
for e.queue.Len() > 0 {
|
|
|
|
for e.queue.Len() > 0 {
|
|
|
|
next := e.queue[0]
|
|
|
|
next := e.queue[0]
|
|
|
|
if next.NextAttackAt.After(now) {
|
|
|
|
if next.NextAttackAt.After(now) {
|
|
|
|
@ -1766,11 +1754,36 @@ func (e *Engine) processHeroAttack(cs *model.CombatState, now time.Time) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
combatEvt := ProcessAttack(cs.Hero, &cs.Enemy, now)
|
|
|
|
combatEvt := ProcessAttack(cs.Hero, &cs.Enemy, now)
|
|
|
|
|
|
|
|
healAmount := hero_actions.UsePotionOnHero(cs.Hero, false)
|
|
|
|
|
|
|
|
// Process all attacks that are due.
|
|
|
|
|
|
|
|
|
|
|
|
e.emitEvent(combatEvt)
|
|
|
|
e.emitEvent(combatEvt)
|
|
|
|
e.logCombatAttack(cs, combatEvt)
|
|
|
|
e.logCombatAttack(cs, combatEvt)
|
|
|
|
|
|
|
|
|
|
|
|
// Push attack envelope.
|
|
|
|
// Push attack envelope.
|
|
|
|
if e.sender != nil {
|
|
|
|
if e.sender != nil {
|
|
|
|
|
|
|
|
if healAmount > 0 {
|
|
|
|
|
|
|
|
e.sender.SendToHero(cs.HeroID, "attack", model.AttackPayload{
|
|
|
|
|
|
|
|
Source: "potion",
|
|
|
|
|
|
|
|
Damage: -healAmount, // negative = heal
|
|
|
|
|
|
|
|
HeroHP: cs.Hero.HP,
|
|
|
|
|
|
|
|
EnemyHP: cs.Enemy.HP,
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
usePotionEvent := model.CombatEvent{
|
|
|
|
|
|
|
|
Type: "attack",
|
|
|
|
|
|
|
|
HeroID: cs.HeroID,
|
|
|
|
|
|
|
|
Damage: healAmount,
|
|
|
|
|
|
|
|
Source: "potion",
|
|
|
|
|
|
|
|
Outcome: attackOutcomeHeal,
|
|
|
|
|
|
|
|
HeroHP: cs.Hero.HP,
|
|
|
|
|
|
|
|
EnemyHP: cs.Enemy.HP,
|
|
|
|
|
|
|
|
Timestamp: now,
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
e.emitEvent(usePotionEvent)
|
|
|
|
|
|
|
|
e.logUsePotion(cs, usePotionEvent)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
e.sender.SendToHero(cs.HeroID, "attack", model.AttackPayload{
|
|
|
|
e.sender.SendToHero(cs.HeroID, "attack", model.AttackPayload{
|
|
|
|
Source: combatEvt.Source,
|
|
|
|
Source: combatEvt.Source,
|
|
|
|
Damage: combatEvt.Damage,
|
|
|
|
Damage: combatEvt.Damage,
|
|
|
|
@ -1883,6 +1896,20 @@ func (e *Engine) logCombatAttack(cs *model.CombatState, evt model.CombatEvent) {
|
|
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (e *Engine) logUsePotion(cs *model.CombatState, evt model.CombatEvent) {
|
|
|
|
|
|
|
|
if e.adventureLog == nil || cs == nil {
|
|
|
|
|
|
|
|
return
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
e.adventureLog(cs.HeroID, model.AdventureLogLine{
|
|
|
|
|
|
|
|
Event: &model.AdventureLogEvent{
|
|
|
|
|
|
|
|
Code: model.LogPhraseUsedHealingPotion,
|
|
|
|
|
|
|
|
Args: map[string]any{"amount": evt.Damage}},
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func (e *Engine) handleEnemyDeath(cs *model.CombatState, now time.Time) {
|
|
|
|
func (e *Engine) handleEnemyDeath(cs *model.CombatState, now time.Time) {
|
|
|
|
hero := cs.Hero
|
|
|
|
hero := cs.Hero
|
|
|
|
enemy := &cs.Enemy
|
|
|
|
enemy := &cs.Enemy
|
|
|
|
|