Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Additional vendor check for keys and scrolls #628

Merged
merged 10 commits into from
Feb 6, 2025
15 changes: 10 additions & 5 deletions internal/action/interaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,18 @@ import (
"github.com/hectorgimenez/koolo/internal/game"
)

func InteractNPC(npc npc.ID) error {
func InteractNPC(NPC npc.ID) error {
ctx := context.Get()
ctx.SetLastAction("InteractNPC")

pos, found := getNPCPosition(npc, ctx.Data)
pos, found := getNPCPosition(NPC, ctx.Data)
if !found {
return fmt.Errorf("npc with ID %d not found", npc)

if NPC == npc.Hratli {
pos = data.Position{X: 5224, Y: 5039}
} else {
return fmt.Errorf("npc with ID %d not found", NPC)
}
}

var err error
Expand All @@ -28,7 +33,7 @@ func InteractNPC(npc npc.ID) error {
continue
}

err = step.InteractNPC(npc)
err = step.InteractNPC(NPC)
if err != nil {
continue
}
Expand All @@ -38,7 +43,7 @@ func InteractNPC(npc npc.ID) error {
return err
}

event.Send(event.InteractedTo(event.Text(ctx.Name, ""), int(npc), event.InteractionTypeNPC))
event.Send(event.InteractedTo(event.Text(ctx.Name, ""), int(NPC), event.InteractionTypeNPC))

return nil
}
Expand Down
21 changes: 21 additions & 0 deletions internal/action/item_pickup.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,27 @@ func shouldBePickedUp(i data.Item) bool {
return true
}

// Check if we should pick up some keys. The goal is to have 12 keys in total (single stack)
if i.Name == "Key" {
quantityOnGround := 0
st, statFound := i.FindStat(stat.Quantity, 0)
if statFound {
quantityOnGround = st.Value
}

quantityInInventory := 0
for _, item := range ctx.Data.Inventory.AllItems {
if item.Name == "Key" {
qty, _ := item.FindStat(stat.Quantity, 0)
quantityInInventory += qty.Value
}
}

if quantityOnGround+quantityInInventory <= 12 {
return true
}
}

// Pick up quest items if we're in leveling or questing run
specialRuns := slices.Contains(ctx.CharacterCfg.Game.Runs, "quests") || slices.Contains(ctx.CharacterCfg.Game.Runs, "leveling")
if specialRuns {
Expand Down
189 changes: 178 additions & 11 deletions internal/action/vendor.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,64 @@ import (
"github.com/hectorgimenez/koolo/internal/town"
"github.com/lxn/win"

"github.com/hectorgimenez/d2go/pkg/data"
"github.com/hectorgimenez/d2go/pkg/data/area"
"github.com/hectorgimenez/d2go/pkg/data/item"
"github.com/hectorgimenez/d2go/pkg/data/npc"
"github.com/hectorgimenez/d2go/pkg/data/stat"
)

func openTradeWindow(vendorNPC npc.ID) error {
ctx := context.Get()

err := InteractNPC(vendorNPC)
if err != nil {
return err
}

// Jamella trade button is the first one
if vendorNPC == npc.Jamella {
ctx.HID.KeySequence(win.VK_HOME, win.VK_RETURN)
} else {
ctx.HID.KeySequence(win.VK_HOME, win.VK_DOWN, win.VK_RETURN)
}

SwitchStashTab(4)
ctx.RefreshGameData()

return nil
}

// Act 1 Vendors:
// Potions: Akara
// Keys: Akara
// Scrolls: Akara
// Arrows/Bolts: Charsi

// Act 2 Vendors:
// Potions: Lysander
// Keys: Lysander
// Scrolls: Drognan
// Arrows/Bolts: Fara

// Act 3 Vendors:
// Potions: Ormus
// Keys: Hratli
// Scrolls: Ormus
// Arrows/Bolts: Hratli

// Act 4 Vendors:
// Potions: Jamella
// Keys: Jamella
// Scrolls: Jamella
// Arrows/Bolts: Halbu

// Act 5 Vendors:
// Potions: Malah
// Keys: Malah
// Scrolls: Malah
// Arrows/Bolts: Larzuk

func VendorRefill(forceRefill, sellJunk bool) error {
ctx := context.Get()
ctx.SetLastAction("VendorRefill")
Expand All @@ -29,26 +83,133 @@ func VendorRefill(forceRefill, sellJunk bool) error {
vendorNPC = npc.Lysander
}
}
err := InteractNPC(vendorNPC)

err := openTradeWindow(vendorNPC)
if err != nil {
return err
}

// Jamella trade button is the first one
if vendorNPC == npc.Jamella {
ctx.HID.KeySequence(win.VK_HOME, win.VK_RETURN)
} else {
ctx.HID.KeySequence(win.VK_HOME, win.VK_DOWN, win.VK_RETURN)
}

SwitchStashTab(4)
ctx.RefreshGameData()
town.BuyConsumables(forceRefill)

if sellJunk {
town.SellJunk()
}

// At this point we are guaranteed to have purchased potions, as the selected vendorNPC will always have these.
// Depending on the act, we may still need keys or scrolls.

if town.ShouldBuyTPs() || town.ShouldBuyIDs() {
restockTomes()
}

if ctx.Data.PlayerUnit.Class != data.Assassin {
_, shouldBuyKeys := town.ShouldBuyKeys()
if shouldBuyKeys {
restockKeys()
}
}

return step.CloseAllMenus()
}

func restockTomes() error {
ctx := context.Get()

shouldBuyTPs := town.ShouldBuyTPs()
shouldBuyIDs := town.ShouldBuyIDs()

if !shouldBuyTPs && !shouldBuyIDs {
return nil
}

ctx.Logger.Info("Visiting vendor to buy scrolls...")

var vendorNPC npc.ID
currentArea := ctx.Data.PlayerUnit.Area
if currentArea == area.RogueEncampment {
vendorNPC = npc.Akara
} else if currentArea == area.LutGholein {
vendorNPC = npc.Drognan
} else if currentArea == area.KurastDocks {
vendorNPC = npc.Ormus
} else if currentArea == area.ThePandemoniumFortress {
vendorNPC = npc.Jamella
} else if currentArea == area.Harrogath {
vendorNPC = npc.Malah
}

if vendorNPC == 0 {
ctx.Logger.Info("Unable to find scroll vendor...")

return nil
}

err := openTradeWindow(vendorNPC)
if err != nil {
return err
}

if shouldBuyTPs {
town.BuyTPs()
}

if shouldBuyIDs {
town.BuyIDs()
}

return nil
}

func restockKeys() error {
ctx := context.Get()

if ctx.Data.PlayerUnit.Class == data.Assassin {
return nil
}

keyQuantity, needsBuy := town.ShouldBuyKeys()

if !needsBuy {
return nil
}

ctx.Logger.Info("Visiting vendor to buy keys...")

var vendorNPC npc.ID
currentArea := ctx.Data.PlayerUnit.Area
if currentArea == area.RogueEncampment {
vendorNPC = npc.Akara
} else if currentArea == area.LutGholein {
vendorNPC = npc.Lysander
} else if currentArea == area.KurastDocks {
vendorNPC = npc.Hratli
} else if currentArea == area.ThePandemoniumFortress {
vendorNPC = npc.Jamella
} else if currentArea == area.Harrogath {
vendorNPC = npc.Malah
}

if vendorNPC == 0 {
ctx.Logger.Info("Unable to find keys vendor...")

return nil
}

err := openTradeWindow(vendorNPC)
if err != nil {
ctx.Logger.Info("Unable to interact with keys vendor", "error", err)
return err
}

if itm, found := ctx.Data.Inventory.Find(item.Key, item.LocationVendor); found {
ctx.Logger.Debug("Vendor with keys detected, provisioning...")

qty, _ := itm.FindStat(stat.Quantity, 0)
if (qty.Value + keyQuantity) <= 12 {
town.BuyFullStack(itm)
}
}

return step.CloseAllMenus()
}

Expand Down Expand Up @@ -102,5 +263,11 @@ func shouldVisitVendor() bool {
return false
}

return ctx.BeltManager.ShouldBuyPotions() || town.ShouldBuyTPs() || town.ShouldBuyIDs()
shouldVisit := ctx.BeltManager.ShouldBuyPotions() || town.ShouldBuyTPs() || town.ShouldBuyIDs()
if shouldVisit {
return true
}

_, shouldBuyKeys := town.ShouldBuyKeys()
return shouldBuyKeys
}
59 changes: 36 additions & 23 deletions internal/town/shop_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,37 @@ import (
"github.com/hectorgimenez/koolo/internal/ui"
)


func BuyTPs() {
ctx := context.Get()

if _, found := ctx.Data.Inventory.Find(item.TomeOfTownPortal, item.LocationInventory); !found {
ctx.Logger.Info("TP Tome not found, buying one...")
if itm, itmFound := ctx.Data.Inventory.Find(item.TomeOfTownPortal, item.LocationVendor); itmFound {
BuyItem(itm, 1)
}
}
ctx.Logger.Debug("Filling TP Tome...")
if itm, found := ctx.Data.Inventory.Find(item.ScrollOfTownPortal, item.LocationVendor); found {
BuyFullStack(itm)
}
}

func BuyIDs() {
ctx := context.Get()

if _, found := ctx.Data.Inventory.Find(item.TomeOfIdentify, item.LocationInventory); !found {
ctx.Logger.Info("ID Tome not found, buying one...")
if itm, itmFound := ctx.Data.Inventory.Find(item.TomeOfIdentify, item.LocationVendor); itmFound {
BuyItem(itm, 1)
}
}
ctx.Logger.Debug("Filling IDs Tome...")
if itm, found := ctx.Data.Inventory.Find(item.ScrollOfIdentify, item.LocationVendor); found {
BuyFullStack(itm)
}
}

func BuyConsumables(forceRefill bool) {
ctx := context.Get()

Expand All @@ -39,29 +70,11 @@ func BuyConsumables(forceRefill bool) {
}

if ShouldBuyTPs() || forceRefill {
if _, found := ctx.Data.Inventory.Find(item.TomeOfTownPortal, item.LocationInventory); !found {
ctx.Logger.Info("TP Tome not found, buying one...")
if itm, itmFound := ctx.Data.Inventory.Find(item.TomeOfTownPortal, item.LocationVendor); itmFound {
BuyItem(itm, 1)
}
}
ctx.Logger.Debug("Filling TP Tome...")
if itm, found := ctx.Data.Inventory.Find(item.ScrollOfTownPortal, item.LocationVendor); found {
buyFullStack(itm)
}
BuyTPs()
}

if ShouldBuyIDs() || forceRefill {
if _, found := ctx.Data.Inventory.Find(item.TomeOfIdentify, item.LocationInventory); !found {
ctx.Logger.Info("ID Tome not found, buying one...")
if itm, itmFound := ctx.Data.Inventory.Find(item.TomeOfIdentify, item.LocationVendor); itmFound {
BuyItem(itm, 1)
}
}
ctx.Logger.Debug("Filling IDs Tome...")
if itm, found := ctx.Data.Inventory.Find(item.ScrollOfIdentify, item.LocationVendor); found {
buyFullStack(itm)
}
BuyIDs()
}

keyQuantity, shouldBuyKeys := ShouldBuyKeys()
Expand All @@ -71,7 +84,7 @@ func BuyConsumables(forceRefill bool) {

qty, _ := itm.FindStat(stat.Quantity, 0)
if (qty.Value + keyQuantity) <= 12 {
buyFullStack(itm)
BuyFullStack(itm)
}
}
}
Expand Down Expand Up @@ -113,7 +126,7 @@ func ShouldBuyIDs() bool {
func ShouldBuyKeys() (int, bool) {
keys, found := context.Get().Data.Inventory.Find(item.Key, item.LocationInventory)
if !found {
return 12, false
return 0, true
sorc653 marked this conversation as resolved.
Show resolved Hide resolved
}

qty, found := keys.FindStat(stat.Quantity, 0)
Expand Down Expand Up @@ -154,7 +167,7 @@ func BuyItem(i data.Item, quantity int) {
}
}

func buyFullStack(i data.Item) {
func BuyFullStack(i data.Item) {
screenPos := ui.GetScreenCoordsForItem(i)

context.Get().HID.ClickWithModifier(game.RightButton, screenPos.X, screenPos.Y, game.ShiftKey)
Expand Down