-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Cain identify fix + improved npc interaction (#631)
- Loading branch information
Showing
2 changed files
with
84 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,88 @@ | ||
package step | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"time" | ||
|
||
"github.com/hectorgimenez/d2go/pkg/data" | ||
"github.com/hectorgimenez/d2go/pkg/data/npc" | ||
"github.com/hectorgimenez/koolo/internal/context" | ||
"github.com/hectorgimenez/koolo/internal/game" | ||
"github.com/hectorgimenez/koolo/internal/pather" | ||
"github.com/hectorgimenez/koolo/internal/ui" | ||
) | ||
|
||
func InteractNPC(npcID npc.ID) error { | ||
maxInteractionAttempts := 5 | ||
interactionAttempts := 0 | ||
waitingForInteraction := false | ||
currentMouseCoords := data.Position{} | ||
lastRun := time.Time{} | ||
|
||
ctx := context.Get() | ||
ctx.SetLastStep("InteractNPC") | ||
|
||
for { | ||
ctx.RefreshGameData() | ||
const ( | ||
maxAttempts = 8 | ||
minMenuOpenWait = 300 * time.Millisecond | ||
maxDistance = 15 | ||
hoverWait = 800 * time.Millisecond | ||
) | ||
|
||
var targetNPCID data.UnitID | ||
|
||
for attempts := 0; attempts < maxAttempts; attempts++ { | ||
// Pause the execution if the priority is not the same as the execution priority | ||
ctx.PauseIfNotPriority() | ||
|
||
if ctx.Data.OpenMenus.NPCInteract { | ||
return nil | ||
} | ||
// Check if interaction succeeded and menu is open | ||
if ctx.Data.OpenMenus.NPCInteract || ctx.Data.OpenMenus.NPCShop { | ||
// Find current NPC position | ||
if targetNPCID != 0 { | ||
if currentNPC, found := ctx.Data.Monsters.FindByID(targetNPCID); found { | ||
currentDistance := pather.DistanceFromPoint(currentNPC.Position, ctx.Data.PlayerUnit.Position) | ||
if currentDistance <= maxDistance { | ||
time.Sleep(minMenuOpenWait) | ||
return nil | ||
} | ||
} | ||
} | ||
|
||
if interactionAttempts >= maxInteractionAttempts { | ||
return errors.New("failed interacting with NPC") | ||
// Wrong NPC, too far, or NPC moved - close menu and retry | ||
CloseAllMenus() | ||
time.Sleep(200 * time.Millisecond) | ||
targetNPCID = 0 | ||
continue | ||
} | ||
|
||
// Give some time before retrying the interaction | ||
if waitingForInteraction && time.Since(lastRun) < time.Millisecond*200 { | ||
townNPC, found := ctx.Data.Monsters.FindOne(npcID, data.MonsterTypeNone) | ||
if !found { | ||
if attempts == maxAttempts-1 { | ||
return fmt.Errorf("NPC %d not found after %d attempts", npcID, maxAttempts) | ||
} | ||
time.Sleep(200 * time.Millisecond) | ||
continue | ||
} | ||
|
||
lastRun = time.Now() | ||
m, found := ctx.Data.Monsters.FindOne(npcID, data.MonsterTypeNone) | ||
if found { | ||
if m.IsHovered { | ||
ctx.HID.Click(game.LeftButton, currentMouseCoords.X, currentMouseCoords.Y) | ||
waitingForInteraction = true | ||
interactionAttempts++ | ||
continue | ||
} | ||
distance := ctx.PathFinder.DistanceFromMe(townNPC.Position) | ||
if distance > maxDistance { | ||
return fmt.Errorf("NPC %d is too far away (distance: %d)", npcID, distance) | ||
} | ||
|
||
distance := ctx.PathFinder.DistanceFromMe(m.Position) | ||
if distance > 15 { | ||
return fmt.Errorf("NPC is too far away: %d. Current distance: %d", npcID, distance) | ||
} | ||
// Calculate click position | ||
x, y := ui.GameCoordsToScreenCords(townNPC.Position.X, townNPC.Position.Y) | ||
if npcID == npc.Tyrael2 { | ||
y = y - 40 // Act 4 Tyrael has a super weird hitbox | ||
} | ||
|
||
// Move mouse and wait for hover | ||
ctx.HID.MovePointer(x, y) | ||
hoverStart := time.Now() | ||
|
||
x, y := ui.GameCoordsToScreenCords(m.Position.X, m.Position.Y) | ||
// Act 4 Tyrael has a super weird hitbox | ||
if npcID == npc.Tyrael2 { | ||
y = y - 40 | ||
for time.Since(hoverStart) < hoverWait { | ||
if currentNPC, found := ctx.Data.Monsters.FindOne(npcID, data.MonsterTypeNone); found && currentNPC.IsHovered { | ||
targetNPCID = currentNPC.UnitID | ||
ctx.HID.Click(game.LeftButton, x, y) | ||
time.Sleep(minMenuOpenWait) | ||
break | ||
} | ||
currentMouseCoords = data.Position{X: x, Y: y} | ||
ctx.HID.MovePointer(x, y) | ||
interactionAttempts++ | ||
time.Sleep(50 * time.Millisecond) | ||
} | ||
} | ||
|
||
return fmt.Errorf("failed to interact with NPC after %d attempts", maxAttempts) | ||
} |