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:
22
pkg/data/mode/npc_mode.go
Normal file
22
pkg/data/mode/npc_mode.go
Normal 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
|
||||||
|
)
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
8
pkg/data/object/portal_type.go
Normal file
8
pkg/data/object/portal_type.go
Normal 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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user