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.
// Should be called each combat tick.
func ProcessEnemyRegen(enemy *model.Enemy, tickDuration time.Duration) int {
// Should be called each combat tick. Uses remainder to avoid per-tick rounding spikes.
func ProcessEnemyRegen(enemy *model.Enemy, tickDuration time.Duration, remainder *float64) int {
if !enemy.HasAbility(model.AbilityRegen) {
return 0
}
@ -368,9 +368,16 @@ func ProcessEnemyRegen(enemy *model.Enemy, tickDuration time.Duration) int {
regenRate = cfg.EnemyRegenBattleLizard
}
healed := int(float64(enemy.MaxHP) * regenRate * tickDuration.Seconds())
if healed < 1 {
healed = 1
healFloat := float64(enemy.MaxHP) * regenRate * tickDuration.Seconds()
if remainder != nil {
healFloat += *remainder
}
healed := int(healFloat)
if remainder != nil {
*remainder = healFloat - float64(healed)
}
if healed <= 0 {
return 0
}
before := enemy.HP
enemy.HP += healed

@ -1170,7 +1170,7 @@ func (e *Engine) processCombatTick(now time.Time) {
}
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)
cs.LastTickAt = now
if regenHealed > 0 && e.sender != nil {

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

@ -33,10 +33,12 @@ type AdminHandler struct {
hub *Hub
pool *pgxpool.Pool
logger *slog.Logger
adminUser string
adminPass string
}
// 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{
store: store,
gearStore: gearStore,
@ -45,6 +47,8 @@ func NewAdminHandler(store *storage.HeroStore, gearStore *storage.GearStore, que
hub: hub,
pool: pool,
logger: logger,
adminUser: adminUser,
adminPass: adminPass,
}
}

@ -22,6 +22,8 @@ type CombatState struct {
EnemyNextAttack time.Time `json:"enemyNextAttack"`
StartedAt time.Time `json:"startedAt"`
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.

@ -68,7 +68,7 @@ func New(deps Deps) *chi.Mux {
r.Post("/api/v1/payments/telegram-webhook", paymentsH.TelegramWebhook)
// 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.Use(handler.BasicAuthMiddleware(handler.BasicAuthConfig{
Username: deps.AdminBasicAuthUsername,
@ -128,6 +128,8 @@ func New(deps Deps) *chi.Mux {
r.Get("/payments/{paymentId}", adminH.GetPayment)
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).
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,
EnemyRegenSkeletonKing: 0.10,
EnemyRegenForestWarden: 0.05,
EnemyRegenBattleLizard: 0.02,
EnemyRegenBattleLizard: 0.01,
SummonCycleSeconds: 15,
SummonDamageDivisor: 4,
LuckBuffMultiplier: 1.75,

Loading…
Cancel
Save