fix regen

master
Denis Ranneft 1 month ago
parent 2d336bfdcd
commit 94d8a0cda8

@ -350,8 +350,8 @@ func ProcessDebuffDamage(hero *model.Hero, tickDuration time.Duration, now time.
} }
// ProcessEnemyRegen handles HP regeneration for enemies with the regen ability. // ProcessEnemyRegen handles HP regeneration for enemies with the regen ability.
// Should be called each combat tick. // Should be called each combat tick. Uses remainder to avoid per-tick rounding spikes.
func ProcessEnemyRegen(enemy *model.Enemy, tickDuration time.Duration) int { func ProcessEnemyRegen(enemy *model.Enemy, tickDuration time.Duration, remainder *float64) int {
if !enemy.HasAbility(model.AbilityRegen) { if !enemy.HasAbility(model.AbilityRegen) {
return 0 return 0
} }
@ -368,9 +368,16 @@ func ProcessEnemyRegen(enemy *model.Enemy, tickDuration time.Duration) int {
regenRate = cfg.EnemyRegenBattleLizard regenRate = cfg.EnemyRegenBattleLizard
} }
healed := int(float64(enemy.MaxHP) * regenRate * tickDuration.Seconds()) healFloat := float64(enemy.MaxHP) * regenRate * tickDuration.Seconds()
if healed < 1 { if remainder != nil {
healed = 1 healFloat += *remainder
}
healed := int(healFloat)
if remainder != nil {
*remainder = healFloat - float64(healed)
}
if healed <= 0 {
return 0
} }
before := enemy.HP before := enemy.HP
enemy.HP += healed enemy.HP += healed

@ -1170,7 +1170,7 @@ func (e *Engine) processCombatTick(now time.Time) {
} }
ProcessDebuffDamage(cs.Hero, tickDur, now) ProcessDebuffDamage(cs.Hero, tickDur, now)
regenHealed := ProcessEnemyRegen(&cs.Enemy, tickDur) regenHealed := ProcessEnemyRegen(&cs.Enemy, tickDur, &cs.EnemyRegenRemainder)
ProcessSummonDamage(cs.Hero, &cs.Enemy, cs.StartedAt, cs.LastTickAt, now) ProcessSummonDamage(cs.Hero, &cs.Enemy, cs.StartedAt, cs.LastTickAt, now)
cs.LastTickAt = now cs.LastTickAt = now
if regenHealed > 0 && e.sender != nil { if regenHealed > 0 && e.sender != nil {

@ -313,6 +313,7 @@ func SimulateOneFight(hero *model.Hero, now time.Time, encounterEnemy *model.Ene
combatStart := now combatStart := now
lastTick := now lastTick := now
var regenRemainder float64
heroNext := now.Add(attackInterval(hero.EffectiveSpeedAt(now))) heroNext := now.Add(attackInterval(hero.EffectiveSpeedAt(now)))
enemyNext := now.Add(attackInterval(enemy.Speed)) enemyNext := now.Add(attackInterval(enemy.Speed))
const maxCombatSteps = 100000 const maxCombatSteps = 100000
@ -330,7 +331,7 @@ func SimulateOneFight(hero *model.Hero, now time.Time, encounterEnemy *model.Ene
tickDur := nextTime.Sub(lastTick) tickDur := nextTime.Sub(lastTick)
if tickDur > 0 { if tickDur > 0 {
ProcessDebuffDamage(hero, tickDur, nextTime) ProcessDebuffDamage(hero, tickDur, nextTime)
ProcessEnemyRegen(&enemy, tickDur) ProcessEnemyRegen(&enemy, tickDur, &regenRemainder)
ProcessSummonDamage(hero, &enemy, combatStart, lastTick, nextTime) ProcessSummonDamage(hero, &enemy, combatStart, lastTick, nextTime)
} }
lastTick = nextTime lastTick = nextTime

@ -33,10 +33,12 @@ type AdminHandler struct {
hub *Hub hub *Hub
pool *pgxpool.Pool pool *pgxpool.Pool
logger *slog.Logger logger *slog.Logger
adminUser string
adminPass string
} }
// NewAdminHandler creates a new AdminHandler with all required dependencies. // NewAdminHandler creates a new AdminHandler with all required dependencies.
func NewAdminHandler(store *storage.HeroStore, gearStore *storage.GearStore, questStore *storage.QuestStore, engine *game.Engine, hub *Hub, pool *pgxpool.Pool, logger *slog.Logger) *AdminHandler { func NewAdminHandler(store *storage.HeroStore, gearStore *storage.GearStore, questStore *storage.QuestStore, engine *game.Engine, hub *Hub, pool *pgxpool.Pool, logger *slog.Logger, adminUser, adminPass string) *AdminHandler {
return &AdminHandler{ return &AdminHandler{
store: store, store: store,
gearStore: gearStore, gearStore: gearStore,
@ -45,6 +47,8 @@ func NewAdminHandler(store *storage.HeroStore, gearStore *storage.GearStore, que
hub: hub, hub: hub,
pool: pool, pool: pool,
logger: logger, logger: logger,
adminUser: adminUser,
adminPass: adminPass,
} }
} }

@ -22,6 +22,8 @@ type CombatState struct {
EnemyNextAttack time.Time `json:"enemyNextAttack"` EnemyNextAttack time.Time `json:"enemyNextAttack"`
StartedAt time.Time `json:"startedAt"` StartedAt time.Time `json:"startedAt"`
LastTickAt time.Time `json:"-"` // tracks previous tick for periodic effects LastTickAt time.Time `json:"-"` // tracks previous tick for periodic effects
// Fractional regen carry between ticks to avoid rounding to 1 HP each tick.
EnemyRegenRemainder float64 `json:"-"`
} }
// AttackEvent is a min-heap entry for scheduling attacks by next_attack_at. // AttackEvent is a min-heap entry for scheduling attacks by next_attack_at.

@ -68,7 +68,7 @@ func New(deps Deps) *chi.Mux {
r.Post("/api/v1/payments/telegram-webhook", paymentsH.TelegramWebhook) r.Post("/api/v1/payments/telegram-webhook", paymentsH.TelegramWebhook)
// Admin routes protected with HTTP Basic authentication. // Admin routes protected with HTTP Basic authentication.
adminH := handler.NewAdminHandler(heroStore, gearStore, questStore, deps.Engine, deps.Hub, deps.PgPool, deps.Logger) adminH := handler.NewAdminHandler(heroStore, gearStore, questStore, deps.Engine, deps.Hub, deps.PgPool, deps.Logger, deps.AdminBasicAuthUsername, deps.AdminBasicAuthPassword)
r.Route("/admin", func(r chi.Router) { r.Route("/admin", func(r chi.Router) {
r.Use(handler.BasicAuthMiddleware(handler.BasicAuthConfig{ r.Use(handler.BasicAuthMiddleware(handler.BasicAuthConfig{
Username: deps.AdminBasicAuthUsername, Username: deps.AdminBasicAuthUsername,
@ -128,6 +128,8 @@ func New(deps Deps) *chi.Mux {
r.Get("/payments/{paymentId}", adminH.GetPayment) r.Get("/payments/{paymentId}", adminH.GetPayment)
r.Post("/payments/set-webhook", paymentsH.SetWebhook) r.Post("/payments/set-webhook", paymentsH.SetWebhook)
}) })
// Admin WebSocket snapshot (auth via query params in handler).
r.Get("/admin-ws/hero/{heroId}", adminH.AdminHeroSnapshotWS)
// API v1 (authenticated routes). // API v1 (authenticated routes).
gameH := handler.NewGameHandler(deps.Engine, heroStore, logStore, worldSvc, deps.Logger, deps.ServerStartedAt, questStore, gearStore, achievementStore, taskStore, deps.Hub) gameH := handler.NewGameHandler(deps.Engine, heroStore, logStore, worldSvc, deps.Logger, deps.ServerStartedAt, questStore, gearStore, achievementStore, taskStore, deps.Hub)

@ -298,7 +298,7 @@ func DefaultValues() Values {
EnemyRegenDefault: 0.02, EnemyRegenDefault: 0.02,
EnemyRegenSkeletonKing: 0.10, EnemyRegenSkeletonKing: 0.10,
EnemyRegenForestWarden: 0.05, EnemyRegenForestWarden: 0.05,
EnemyRegenBattleLizard: 0.02, EnemyRegenBattleLizard: 0.01,
SummonCycleSeconds: 15, SummonCycleSeconds: 15,
SummonDamageDivisor: 4, SummonDamageDivisor: 4,
LuckBuffMultiplier: 1.75, LuckBuffMultiplier: 1.75,

Loading…
Cancel
Save