Skip to content

Commit

Permalink
Improve keep distance while attacking
Browse files Browse the repository at this point in the history
EnsureEnemyIsInRange:
Allow precise close movements if distanceToMove < DistanceToFinishMoving ( wich is 4)   but only if distanceToMove >= 2 .   Otherwise overshoot like before to avoid micro movements.  Way less telestomp.

Also changed cleanup of checkMonsterDamage  to prevent possible memory leak.
  • Loading branch information
elobo91 committed Jan 26, 2025
1 parent 5c53c0d commit 384b13e
Showing 1 changed file with 31 additions and 17 deletions.
48 changes: 31 additions & 17 deletions internal/action/step/attack.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package step
import (
"errors"
"fmt"
"sync"
"time"

"github.com/hectorgimenez/d2go/pkg/data"
Expand All @@ -16,6 +17,11 @@ import (

const attackCycleDuration = 120 * time.Millisecond

var (
statesMutex sync.RWMutex
monsterStates = make(map[data.UnitID]*attackState)
)

// Contains all configuration for an attack sequence
type attackSettings struct {
primaryAttack bool // Whether this is a primary (left click) attack
Expand All @@ -41,8 +47,6 @@ type attackState struct {
position data.Position
}

var monsterAttackStates = make(map[data.UnitID]*attackState)

// Distance configures attack to follow enemy within specified range
func Distance(minimum, maximum int) AttackOption {
return func(step *attackSettings) {
Expand Down Expand Up @@ -296,7 +300,7 @@ func ensureEnemyIsInRange(monster data.Monster, maxDistance, minDistance int, ne
hasLoS := ctx.PathFinder.LineOfSight(currentPos, monster.Position)

// We have line of sight, and we are inside the attack range, we can skip
if hasLoS && distanceToMonster <= maxDistance && distanceToMonster >= minDistance && !needsRepositioning {
if hasLoS && distanceToMonster <= maxDistance && !needsRepositioning {
return nil
}
// Handle repositioning if needed
Expand Down Expand Up @@ -334,13 +338,16 @@ func ensureEnemyIsInRange(monster data.Monster, maxDistance, minDistance int, ne
Y: pos.Y + ctx.Data.AreaData.OffsetY,
}

// Handle overshooting for short distances (Nova distances)
distanceToMove := ctx.PathFinder.DistanceFromMe(dest)
if distanceToMove <= DistanceToFinishMoving {
dest = ctx.PathFinder.BeyondPosition(currentPos, dest, 9)
}

if ctx.PathFinder.LineOfSight(dest, monster.Position) {
distanceToMove := ctx.PathFinder.DistanceFromMe(dest)
if distanceToMove < DistanceToFinishMoving {
// Allow precise close movements
if distanceToMove >= 2 {
return MoveTo(dest, WithDistanceToFinish(2))
}
// Otherwise overshoot to avoid micro-movements
dest = ctx.PathFinder.BeyondPosition(currentPos, dest, 9)
}
return MoveTo(dest)
}
}
Expand All @@ -349,37 +356,44 @@ func ensureEnemyIsInRange(monster data.Monster, maxDistance, minDistance int, ne
}

func checkMonsterDamage(monster data.Monster) (bool, *attackState) {
state, exists := monsterAttackStates[monster.UnitID]
statesMutex.Lock()
defer statesMutex.Unlock()

state, exists := monsterStates[monster.UnitID]
if !exists {
state = &attackState{
lastHealth: monster.Stats[stat.Life],
lastHealthCheckTime: time.Now(),
position: monster.Position,
}
monsterAttackStates[monster.UnitID] = state
monsterStates[monster.UnitID] = state
}

didDamage := false
currentHealth := monster.Stats[stat.Life]

// Only update health check if some time has passed
if time.Since(state.lastHealthCheckTime) > 100*time.Millisecond {
if currentHealth < state.lastHealth {
didDamage = true
state.failedAttemptStartTime = time.Time{}
} else if state.failedAttemptStartTime.IsZero() &&
monster.Position == state.position { // only start failing if monster hasn't moved
monster.Position == state.position {
state.failedAttemptStartTime = time.Now()
}

state.lastHealth = currentHealth
state.lastHealthCheckTime = time.Now()
state.position = monster.Position
}

// Clean up state map occasionally
if len(monsterAttackStates) > 100 {
monsterAttackStates = make(map[data.UnitID]*attackState)
// Clean up old entries periodically
if len(monsterStates) > 100 {
now := time.Now()
for id, s := range monsterStates {
if now.Sub(s.lastHealthCheckTime) > 5*time.Minute {
delete(monsterStates, id)
}
}
}
}

return didDamage, state
Expand Down

0 comments on commit 384b13e

Please sign in to comment.