Npc/Monsters Mode and Portals destination + optimization (#49)

* Added States for monsters and corpses

Now can properly identify if a corpse is interactable with a skill ( barbarian, necromancer)
Also getting states about monsters  : Exemple  Soul Killer with Conviction aura:   State  28 // conviction

Also can tell if monster has a state after you used a skill on it  Exemple:   State 56  Terror ( After using Barbarian skill Howl to fear )

Another Exemple  using Grief on Mephistos he now have  states :    2 poison
    52  Preventheal

will be very useful for further development.

* Added modes to player

Now accessible in koolo using : s.data.PlayerUnit.Mode

Those are Modes for player:
const Death: 0;
        const StandingOutsideTown: 1;
        const Walking: 2;
        const Running: 3;
        const GettingHit: 4;
        const StandingInTown: 5;
        const WalkingInTown: 6;
        const Attacking1: 7;
        const Attacking2: 8;
        const Blocking: 9;
        const CastingSkill: 10;
        const ThrowingItem: 11;
        const Kicking: 12;
        const UsingSkill1: 13;
        const UsingSkill2: 14;
        const UsingSkill3: 15;
        const UsingSkill4: 16;
        const Dead: 17;
        const SkillActionSequence: 18;
        const KnockedBack: 19;

* update Added alias(const) for player modes

can be used like this s.data.PlayerUnit.Mode == mode.StandingInTown

* added modes to object

0 = Object idle
1 = Object operating
2 = Object opened
3 = Object special 1    ?? to be determined what it does exactly
4 = Object special 2    ?? to be determined what it does exactly
5 = Object special 3    ?? to be determined what it does exactly
6 = Object special 4    ?? to be determined what it does exactly
7 = Object special 5    ?? to be determined what it does exactly

* update object mode strings

* Update object_mode.go

* Update object_mode.go

* update object modes again. accurate now

* Added IsSealBoss()  for Chaos Run

* Npc/Monster Modes and Portals destination
also IsEscapingType()  Monster cannot be attacked when airborne or hide in water (NpcMode 8)

* Npc/Monster Modes and Portals destination
also IsEscapingType()  Monster cannot be attacked when airborne or hide in water (NpcMode 8)

* Optimized monsters.go and object.go
also catapults have been added to ignore list

* Tz Frigid Higland ingores added
This commit is contained in:
elb
2024-12-01 08:54:57 -05:00
committed by GitHub
parent b2c710dc5c
commit 2eeee0cf1f
6 changed files with 218 additions and 148 deletions

22
pkg/data/mode/npc_mode.go Normal file
View File

@@ -0,0 +1,22 @@
package mode
type NpcMode uint32
const (
NpcDeath NpcMode = iota
NpcStandingStill
NpcWalking
NpcGettingHit
NpcAttacking1
NpcAttacking2
NpcBlocking
NpcCastingSpell
NpcUsingSkill1
NpcUsingSkill2
NpcUsingSkill3
NpcUsingSkill4
NpcDead
NpcKnockedBack
NpcActionSequence
NpcRunning
)

View File

@@ -1,6 +1,7 @@
package data package data
import ( import (
"github.com/hectorgimenez/d2go/pkg/data/mode"
"github.com/hectorgimenez/d2go/pkg/data/npc" "github.com/hectorgimenez/d2go/pkg/data/npc"
"github.com/hectorgimenez/d2go/pkg/data/stat" "github.com/hectorgimenez/d2go/pkg/data/stat"
"github.com/hectorgimenez/d2go/pkg/data/state" "github.com/hectorgimenez/d2go/pkg/data/state"
@@ -22,6 +23,7 @@ type Monster struct {
Stats map[stat.ID]int Stats map[stat.ID]int
Type MonsterType Type MonsterType
States state.States States state.States
Mode mode.NpcMode
} }
type Monsters []Monster type Monsters []Monster
@@ -177,9 +179,26 @@ func (m Monster) IsMonsterRaiser() bool {
// IsSkip monster can not be killed as a normal enemy, for example can not be targeted // IsSkip monster can not be killed as a normal enemy, for example can not be targeted
func (m Monster) IsSkip() bool { func (m Monster) IsSkip() bool {
switch m.Name { switch m.Name {
case npc.WaterWatcherLimb, npc.WaterWatcherHead, npc.BaalTaunt, npc.Act5Combatant, npc.Act5Combatant2, npc.BarricadeTower: case npc.WaterWatcherLimb, npc.WaterWatcherHead, npc.BaalTaunt, npc.Act5Combatant, npc.Act5Combatant2, npc.BarricadeTower, npc.DarkWanderer, npc.POW:
return true return true
} }
return false return false
} }
func (m Monster) IsSealBoss() bool {
return (m.Type == MonsterTypeSuperUnique || m.Type == MonsterTypeMinion) && (m.Name == npc.OblivionKnight || (m.Name == npc.DoomKnight) || // Lord De Seis
m.Name == npc.VenomLord || // Infector of Souls
m.Name == npc.StormCaster) // Grand Vizier of Chaos
}
// IsEscapingType Monster cannot be attacked when airborne or hide in water (NpcMode 8)
func (m Monster) IsEscapingType() bool {
switch m.Name {
case npc.CarrionBird, npc.CarrionBird2, npc.WaterWatcherLimb, npc.RiverStalkerLimb, npc.StygianWatcherLimb,
npc.WaterWatcherHead, npc.RiverStalkerHead, npc.StygianWatcherHead, npc.CloudStalker, npc.Sucker, npc.UndeadScavenger, npc.FoulCrow:
return true
}
return false
}

View File

@@ -0,0 +1,8 @@
package object
import "github.com/hectorgimenez/d2go/pkg/data/area"
type PortalData struct {
DestArea area.ID
//there is more to discover
}

View File

@@ -15,6 +15,7 @@ type Object struct {
Position Position Position Position
Owner string Owner string
Mode mode.ObjectMode Mode mode.ObjectMode
PortalData object.PortalData
} }
type Objects []Object type Objects []Object
@@ -25,7 +26,6 @@ func (o Objects) FindOne(name object.Name) (Object, bool) {
return obj, true return obj, true
} }
} }
return Object{}, false return Object{}, false
} }
@@ -35,7 +35,6 @@ func (o Objects) FindByID(id UnitID) (Object, bool) {
return obj, true return obj, true
} }
} }
return Object{}, false return Object{}, false
} }
@@ -67,7 +66,6 @@ func (o Object) IsShrine() bool {
} }
return false return false
} }
func (o Object) IsWaypoint() bool { func (o Object) IsWaypoint() bool {
switch o.Name { switch o.Name {
case object.WaypointPortal, case object.WaypointPortal,
@@ -88,7 +86,6 @@ func (o Object) IsWaypoint() bool {
object.ExpansionWaypoint: object.ExpansionWaypoint:
return true return true
} }
return false return false
} }
@@ -110,7 +107,6 @@ func (o Object) IsChest() bool {
502, 504, 505, 518, 524, 525, 526, 529, 530, 531, 532, 533, 534, 535, 540, 541, 544, 545, 556, 580, 581: 502, 504, 505, 518, 524, 525, 526, 529, 530, 531, 532, 533, 534, 535, 540, 541, 544, 545, 556, 580, 581:
return true return true
} }
return false return false
} }
func (o Object) IsDoor() bool { func (o Object) IsDoor() bool {
@@ -145,7 +141,6 @@ func (o Object) IsDoor() bool {
object.ExpansionTownGate: object.ExpansionTownGate:
return true return true
} }
return false return false
} }
@@ -154,6 +149,5 @@ func (o Object) IsSuperChest() bool {
case 104, 105, 106, 107, 181, 183, 580, 397, 387, 389, 390, 391, 455: case 104, 105, 106, 107, 181, 183, 580, 397, 387, 389, 390, 391, 455:
return true return true
} }
return false return false
} }

View File

@@ -3,6 +3,8 @@ package memory
import ( import (
"sort" "sort"
"github.com/hectorgimenez/d2go/pkg/data/mode"
"github.com/hectorgimenez/d2go/pkg/data" "github.com/hectorgimenez/d2go/pkg/data"
"github.com/hectorgimenez/d2go/pkg/data/npc" "github.com/hectorgimenez/d2go/pkg/data/npc"
"github.com/hectorgimenez/d2go/pkg/data/stat" "github.com/hectorgimenez/d2go/pkg/data/stat"
@@ -18,19 +20,27 @@ func (gd *GameReader) Monsters(playerPosition data.Position, hover data.HoverDat
monsterOffset := 8 * i monsterOffset := 8 * i
monsterUnitPtr := uintptr(ReadUIntFromBuffer(unitTableBuffer, uint(monsterOffset), Uint64)) monsterUnitPtr := uintptr(ReadUIntFromBuffer(unitTableBuffer, uint(monsterOffset), Uint64))
for monsterUnitPtr > 0 { for monsterUnitPtr > 0 {
monsterDataBuffer := gd.Process.ReadBytesFromMemory(monsterUnitPtr, 144) // Quick corpse check first
isCorpse := gd.Process.ReadUInt(monsterUnitPtr+0x1A6, Uint8)
if isCorpse != 0 {
monsterUnitPtr = uintptr(gd.Process.ReadUInt(monsterUnitPtr+0x150, Uint64))
continue
}
//monsterType := ReadUIntFromBuffer(monsterDataBuffer, 0x00, Uint32) monsterDataBuffer := gd.Process.ReadBytesFromMemory(monsterUnitPtr, 144)
txtFileNo := ReadUIntFromBuffer(monsterDataBuffer, 0x04, Uint32) txtFileNo := ReadUIntFromBuffer(monsterDataBuffer, 0x04, Uint32)
unitID := ReadUIntFromBuffer(monsterDataBuffer, 0x08, Uint32) unitID := ReadUIntFromBuffer(monsterDataBuffer, 0x08, Uint32)
//mode := ReadUIntFromBuffer(monsterDataBuffer, 0x0C, Uint32) // Get stats early for filtering
statsListExPtr := uintptr(ReadUIntFromBuffer(monsterDataBuffer, 0x88, Uint64))
statPtr := uintptr(gd.Process.ReadUInt(statsListExPtr+0x30, Uint64))
statCount := gd.Process.ReadUInt(statsListExPtr+0x38, Uint64)
stats := gd.getMonsterStats(statCount, statPtr)
if !gd.shouldBeIgnored(txtFileNo) || stats[stat.Experience] > 0 {
monsterMode := mode.NpcMode(gd.Process.ReadUInt(monsterUnitPtr+0x0c, Uint32))
unitDataPtr := uintptr(ReadUIntFromBuffer(monsterDataBuffer, 0x10, Uint64)) unitDataPtr := uintptr(ReadUIntFromBuffer(monsterDataBuffer, 0x10, Uint64))
//isUnique := gd.Process.ReadUInt(unitDataPtr+0x18, Uint16)
flag := gd.Process.ReadBytesFromMemory(unitDataPtr+0x1A, Uint8)[0] flag := gd.Process.ReadBytesFromMemory(unitDataPtr+0x1A, Uint8)[0]
isCorpse := gd.Process.ReadUInt(monsterUnitPtr+0x1A6, Uint8)
//unitDataBuffer := gd.Process.ReadBytesFromMemory(unitDataPtr, 144) //unitDataBuffer := gd.Process.ReadBytesFromMemory(unitDataPtr, 144)
// Coordinates (X, Y) // Coordinates (X, Y)
@@ -38,24 +48,12 @@ func (gd *GameReader) Monsters(playerPosition data.Position, hover data.HoverDat
posX := gd.Process.ReadUInt(pathPtr+0x02, Uint16) posX := gd.Process.ReadUInt(pathPtr+0x02, Uint16)
posY := gd.Process.ReadUInt(pathPtr+0x06, Uint16) posY := gd.Process.ReadUInt(pathPtr+0x06, Uint16)
hovered := false
if hover.IsHovered && hover.UnitType == 1 && hover.UnitID == data.UnitID(unitID) {
hovered = true
}
statsListExPtr := uintptr(ReadUIntFromBuffer(monsterDataBuffer, 0x88, Uint64))
statPtr := uintptr(gd.Process.ReadUInt(statsListExPtr+0x30, Uint64))
statCount := gd.Process.ReadUInt(statsListExPtr+0x38, Uint64)
stats := gd.getMonsterStats(statCount, statPtr)
states := gd.GetStates(statsListExPtr) states := gd.GetStates(statsListExPtr)
// This excludes good NPCs but includes Mercs monsters = append(monsters, data.Monster{
if !gd.shouldBeIgnored(txtFileNo) || stats[stat.Experience] > 0 {
m := data.Monster{
UnitID: data.UnitID(unitID), UnitID: data.UnitID(unitID),
Name: npc.ID(int(txtFileNo)), Name: npc.ID(int(txtFileNo)),
IsHovered: hovered, IsHovered: hover.IsHovered && hover.UnitType == 1 && hover.UnitID == data.UnitID(unitID),
Position: data.Position{ Position: data.Position{
X: int(posX), X: int(posX),
Y: int(posY), Y: int(posY),
@@ -63,27 +61,89 @@ func (gd *GameReader) Monsters(playerPosition data.Position, hover data.HoverDat
Stats: stats, Stats: stats,
Type: getMonsterType(flag), Type: getMonsterType(flag),
States: states, States: states,
} Mode: monsterMode,
})
if isCorpse == 0 {
monsters = append(monsters, m)
}
} }
monsterUnitPtr = uintptr(gd.Process.ReadUInt(monsterUnitPtr+0x150, Uint64)) monsterUnitPtr = uintptr(gd.Process.ReadUInt(monsterUnitPtr+0x150, Uint64))
} }
} }
if len(monsters) > 0 {
sort.SliceStable(monsters, func(i, j int) bool { sort.SliceStable(monsters, func(i, j int) bool {
distanceI := utils.DistanceFromPoint(playerPosition, monsters[i].Position) distanceI := utils.DistanceFromPoint(playerPosition, monsters[i].Position)
distanceJ := utils.DistanceFromPoint(playerPosition, monsters[j].Position) distanceJ := utils.DistanceFromPoint(playerPosition, monsters[j].Position)
return distanceI < distanceJ return distanceI < distanceJ
}) })
}
return monsters return monsters
} }
func (gd *GameReader) Corpses(playerPosition data.Position, hover data.HoverData) data.Monsters {
baseAddr := gd.Process.moduleBaseAddressPtr + gd.offset.UnitTable + 1024
unitTableBuffer := gd.Process.ReadBytesFromMemory(baseAddr, 128*8)
corpses := data.Monsters{}
for i := 0; i < 128; i++ {
monsterOffset := 8 * i
monsterUnitPtr := uintptr(ReadUIntFromBuffer(unitTableBuffer, uint(monsterOffset), Uint64))
for monsterUnitPtr > 0 {
isCorpse := gd.Process.ReadUInt(monsterUnitPtr+0x1A6, Uint8)
if isCorpse == 0 {
monsterUnitPtr = uintptr(gd.Process.ReadUInt(monsterUnitPtr+0x150, Uint64))
continue
}
monsterDataBuffer := gd.Process.ReadBytesFromMemory(monsterUnitPtr, 144)
txtFileNo := ReadUIntFromBuffer(monsterDataBuffer, 0x04, Uint32)
statsListExPtr := uintptr(ReadUIntFromBuffer(monsterDataBuffer, 0x88, Uint64))
statPtr := uintptr(gd.Process.ReadUInt(statsListExPtr+0x30, Uint64))
statCount := gd.Process.ReadUInt(statsListExPtr+0x38, Uint64)
stats := gd.getMonsterStats(statCount, statPtr)
if !gd.shouldBeIgnored(txtFileNo) || stats[stat.Experience] > 0 {
unitID := ReadUIntFromBuffer(monsterDataBuffer, 0x08, Uint32)
unitDataPtr := uintptr(ReadUIntFromBuffer(monsterDataBuffer, 0x10, Uint64))
flag := gd.Process.ReadBytesFromMemory(unitDataPtr+0x1A, Uint8)[0]
pathPtr := uintptr(gd.Process.ReadUInt(monsterUnitPtr+0x38, Uint64))
posX := gd.Process.ReadUInt(pathPtr+0x02, Uint16)
posY := gd.Process.ReadUInt(pathPtr+0x06, Uint16)
states := gd.GetStates(statsListExPtr)
corpses = append(corpses, data.Monster{
UnitID: data.UnitID(unitID),
Name: npc.ID(int(txtFileNo)),
IsHovered: hover.IsHovered && hover.UnitType == 1 && hover.UnitID == data.UnitID(unitID),
Position: data.Position{
X: int(posX),
Y: int(posY),
},
Stats: stats,
Type: getMonsterType(flag),
States: states,
})
}
monsterUnitPtr = uintptr(gd.Process.ReadUInt(monsterUnitPtr+0x150, Uint64))
}
}
if len(corpses) > 0 {
sort.SliceStable(corpses, func(i, j int) bool {
distanceI := utils.DistanceFromPoint(playerPosition, corpses[i].Position)
distanceJ := utils.DistanceFromPoint(playerPosition, corpses[j].Position)
return distanceI < distanceJ
})
}
return corpses
}
func getMonsterType(typeFlag byte) data.MonsterType { func getMonsterType(typeFlag byte) data.MonsterType {
switch typeFlag { switch typeFlag {
case 10: case 10:
@@ -95,7 +155,6 @@ func getMonsterType(typeFlag byte) data.MonsterType {
case 1 << 4: case 1 << 4:
return data.MonsterTypeMinion return data.MonsterTypeMinion
} }
return data.MonsterTypeNone return data.MonsterTypeNone
} }
@@ -115,73 +174,6 @@ func (gd *GameReader) getMonsterStats(statCount uint, statPtr uintptr) map[stat.
return stats return stats
} }
func (gd *GameReader) Corpses(playerPosition data.Position, hover data.HoverData) data.Monsters {
baseAddr := gd.Process.moduleBaseAddressPtr + gd.offset.UnitTable + 1024
unitTableBuffer := gd.Process.ReadBytesFromMemory(baseAddr, 128*8)
corpses := data.Monsters{}
for i := 0; i < 128; i++ {
monsterOffset := 8 * i
monsterUnitPtr := uintptr(ReadUIntFromBuffer(unitTableBuffer, uint(monsterOffset), Uint64))
for monsterUnitPtr > 0 {
monsterDataBuffer := gd.Process.ReadBytesFromMemory(monsterUnitPtr, 144)
txtFileNo := ReadUIntFromBuffer(monsterDataBuffer, 0x04, Uint32)
unitID := ReadUIntFromBuffer(monsterDataBuffer, 0x08, Uint32)
unitDataPtr := uintptr(ReadUIntFromBuffer(monsterDataBuffer, 0x10, Uint64))
flag := gd.Process.ReadBytesFromMemory(unitDataPtr+0x1A, Uint8)[0]
isCorpse := gd.Process.ReadUInt(monsterUnitPtr+0x1A6, Uint8)
if isCorpse == 0 {
monsterUnitPtr = uintptr(gd.Process.ReadUInt(monsterUnitPtr+0x150, Uint64))
continue
}
pathPtr := uintptr(gd.Process.ReadUInt(monsterUnitPtr+0x38, Uint64))
posX := gd.Process.ReadUInt(pathPtr+0x02, Uint16)
posY := gd.Process.ReadUInt(pathPtr+0x06, Uint16)
statsListExPtr := uintptr(ReadUIntFromBuffer(monsterDataBuffer, 0x88, Uint64))
statPtr := uintptr(gd.Process.ReadUInt(statsListExPtr+0x30, Uint64))
statCount := gd.Process.ReadUInt(statsListExPtr+0x38, Uint64)
stats := gd.getMonsterStats(statCount, statPtr)
states := gd.GetStates(statsListExPtr)
hovered := hover.IsHovered && hover.UnitType == 1 && hover.UnitID == data.UnitID(unitID)
if (!gd.shouldBeIgnored(txtFileNo) || stats[stat.Experience] > 0) && isCorpse != 0 {
m := data.Monster{
UnitID: data.UnitID(unitID),
Name: npc.ID(int(txtFileNo)),
IsHovered: hovered,
Position: data.Position{
X: int(posX),
Y: int(posY),
},
Stats: stats,
Type: getMonsterType(flag),
States: states,
}
corpses = append(corpses, m)
}
monsterUnitPtr = uintptr(gd.Process.ReadUInt(monsterUnitPtr+0x150, Uint64))
}
}
sort.SliceStable(corpses, func(i, j int) bool {
distanceI := utils.DistanceFromPoint(playerPosition, corpses[i].Position)
distanceJ := utils.DistanceFromPoint(playerPosition, corpses[j].Position)
return distanceI < distanceJ
})
return corpses
}
func (gd *GameReader) shouldBeIgnored(txtNo uint) bool { func (gd *GameReader) shouldBeIgnored(txtNo uint) bool {
switch npc.ID(txtNo) { switch npc.ID(txtNo) {
case npc.Chicken, case npc.Chicken,
@@ -247,9 +239,23 @@ func (gd *GameReader) shouldBeIgnored(txtNo uint) bool {
npc.InjuredBarbarian, npc.InjuredBarbarian,
npc.InjuredBarbarian2, npc.InjuredBarbarian2,
npc.InjuredBarbarian3, npc.InjuredBarbarian3,
npc.FrenziedHellSpawn,
npc.FrenziedIceSpawn,
npc.CatapultSpotterE,
npc.CatapultSpotterS,
npc.CatapultSpotterW,
npc.CatapultW,
npc.CatapultE,
npc.CatapultS,
npc.CatapultSiege,
npc.BarricadeWallRight,
npc.BarricadeWallLeft,
npc.BarricadeDoor,
npc.BarricadeDoor2,
npc.BarricadeTower,
npc.EvilHut,
npc.DemonHole: npc.DemonHole:
return true return true
} }
return false return false
} }

View File

@@ -1,53 +1,73 @@
package memory package memory
import ( import (
"github.com/hectorgimenez/d2go/pkg/data/mode"
"sort" "sort"
"github.com/hectorgimenez/d2go/pkg/data/mode"
"github.com/hectorgimenez/d2go/pkg/data" "github.com/hectorgimenez/d2go/pkg/data"
"github.com/hectorgimenez/d2go/pkg/data/area"
"github.com/hectorgimenez/d2go/pkg/data/object" "github.com/hectorgimenez/d2go/pkg/data/object"
"github.com/hectorgimenez/d2go/pkg/utils" "github.com/hectorgimenez/d2go/pkg/utils"
) )
func isPortal(txtFileNo int) bool {
desc, ok := object.Desc[txtFileNo]
return ok && desc.Name == "Portal"
}
func (gd *GameReader) Objects(playerPosition data.Position, hover data.HoverData) []data.Object { func (gd *GameReader) Objects(playerPosition data.Position, hover data.HoverData) []data.Object {
baseAddr := gd.Process.moduleBaseAddressPtr + gd.offset.UnitTable + (2 * 1024) baseAddr := gd.Process.moduleBaseAddressPtr + gd.offset.UnitTable + (2 * 1024)
unitTableBuffer := gd.Process.ReadBytesFromMemory(baseAddr, 128*8) unitTableBuffer := gd.Process.ReadBytesFromMemory(baseAddr, 128*8)
var objects []data.Object var objects []data.Object
for i := 0; i < 128; i++ { for i := 0; i < 128; i++ {
objectOffset := 8 * i objectOffset := 8 * i
objectUnitPtr := uintptr(ReadUIntFromBuffer(unitTableBuffer, uint(objectOffset), Uint64)) objectUnitPtr := uintptr(ReadUIntFromBuffer(unitTableBuffer, uint(objectOffset), Uint64))
for objectUnitPtr > 0 {
objectType := gd.Process.ReadUInt(objectUnitPtr+0x00, Uint32)
if objectType == 2 {
txtFileNo := gd.Process.ReadUInt(objectUnitPtr+0x04, Uint32)
unitID := gd.Process.ReadUInt(objectUnitPtr+0x08, Uint32)
// Read the object mode for objectUnitPtr > 0 {
// Read minimal data first to check object type
objectType := gd.Process.ReadUInt(objectUnitPtr+0x00, Uint32)
if objectType == 2 {
rawTxtFileNo := gd.Process.ReadUInt(objectUnitPtr+0x04, Uint32) // Extract actual txtFileNo
txtFileNo := rawTxtFileNo & 0xFFFF
unitID := gd.Process.ReadUInt(objectUnitPtr+0x08, Uint32)
objectMode := mode.ObjectMode(gd.Process.ReadUInt(objectUnitPtr+0x0c, Uint32)) objectMode := mode.ObjectMode(gd.Process.ReadUInt(objectUnitPtr+0x0c, Uint32))
// Coordinates (X, Y) unitDataPtr := uintptr(gd.Process.ReadUInt(objectUnitPtr+0x10, Uint64))
//This offset gives timer for each mode to keep progress in real time. exemple: Mode.Operating fresh timer then Mode.Opened new timer (for objects)
// timerValue := uint32(gd.Process.ReadUInt(objectUnitPtr+0x5C, Uint32))
// Path and position data
pathPtr := uintptr(gd.Process.ReadUInt(objectUnitPtr+0x38, Uint64)) pathPtr := uintptr(gd.Process.ReadUInt(objectUnitPtr+0x38, Uint64))
// Coordinates (X, Y)
posX := gd.Process.ReadUInt(pathPtr+0x10, Uint16) posX := gd.Process.ReadUInt(pathPtr+0x10, Uint16)
posY := gd.Process.ReadUInt(pathPtr+0x14, Uint16) posY := gd.Process.ReadUInt(pathPtr+0x14, Uint16)
unitDataPtr := uintptr(gd.Process.ReadUInt(objectUnitPtr+0x10, Uint64)) var shrineData object.ShrineData
// checking if this is a shrine var portalData object.PortalData
interactType := gd.Process.ReadUInt(unitDataPtr+0x08, Uint8)
owner := gd.Process.ReadStringFromMemory(unitDataPtr+0x34, 32)
// Handle portals
if isPortal(int(txtFileNo)) {
destArea := area.ID(gd.Process.ReadUInt(unitDataPtr+0x08, Uint8))
portalData.DestArea = destArea
// Handle Shrines
} else {
shrineTextPtr := uintptr(gd.Process.ReadUInt(objectUnitPtr+0x0A, Uint64)) shrineTextPtr := uintptr(gd.Process.ReadUInt(objectUnitPtr+0x0A, Uint64))
shrineType := uint(0)
interactType := uint(0)
shrineData := object.ShrineData{}
if shrineTextPtr > 0 { if shrineTextPtr > 0 {
shrineType = gd.Process.ReadUInt(unitDataPtr+0x08, Uint8) shrineType := gd.Process.ReadUInt(unitDataPtr+0x08, Uint8)
shrineData = object.ShrineData{ shrineData = object.ShrineData{
ShrineName: object.ShrineTypeNames[object.ShrineType(shrineType)], ShrineName: object.ShrineTypeNames[object.ShrineType(shrineType)],
ShrineType: object.ShrineType(shrineType), ShrineType: object.ShrineType(shrineType),
} }
} else {
interactType = gd.Process.ReadUInt(unitDataPtr+0x08, Uint8)
} }
owner := gd.Process.ReadStringFromMemory(unitDataPtr+0x34, 32) }
// Handle objects
obj := data.Object{ objects = append(objects, data.Object{
ID: data.UnitID(unitID), ID: data.UnitID(unitID),
Name: object.Name(int(txtFileNo)), Name: object.Name(int(txtFileNo)),
IsHovered: data.UnitID(unitID) == hover.UnitID && hover.UnitType == 2 && hover.IsHovered, IsHovered: data.UnitID(unitID) == hover.UnitID && hover.UnitType == 2 && hover.IsHovered,
@@ -60,19 +80,20 @@ func (gd *GameReader) Objects(playerPosition data.Position, hover data.HoverData
}, },
Owner: owner, Owner: owner,
Mode: objectMode, Mode: objectMode,
} PortalData: portalData,
objects = append(objects, obj) })
} }
objectUnitPtr = uintptr(gd.Process.ReadUInt(objectUnitPtr+0x150, Uint64)) objectUnitPtr = uintptr(gd.Process.ReadUInt(objectUnitPtr+0x150, Uint64))
} }
} }
if len(objects) > 0 {
sort.SliceStable(objects, func(i, j int) bool { sort.SliceStable(objects, func(i, j int) bool {
distanceI := utils.DistanceFromPoint(playerPosition, objects[i].Position) distanceI := utils.DistanceFromPoint(playerPosition, objects[i].Position)
distanceJ := utils.DistanceFromPoint(playerPosition, objects[j].Position) distanceJ := utils.DistanceFromPoint(playerPosition, objects[j].Position)
return distanceI < distanceJ return distanceI < distanceJ
}) })
}
return objects return objects
} }