Fix broken negative stats, update some stat description (#79)

* Fix broken negative stats, update some stat description

* ###

* #2

* curses is necro, frw on armor is velocity percent ( unused)

* Update rules to accept negative value

fix a bug with unique grand charm tz

[name] == grandcharm && [quality] == unique # ([fireresist] + [coldresist] + [lightresist]) >= -75

Single stat rules like [coldresist] >= -75 require the stat to exist

expressions like ([fireresist] + [coldresist] + [lightresist]) >= -75 treat missing stats as 0

Unidentified items with any stat requirements need identification first

* LevelReq + identifiedName added for all Crafted items

* Fix issue with rule.go

* Update stat throw damage and basename: GrandCharm -- Grand Charm

* Adjusting rule for ItemLevelReq  = 0

* update rule again
This commit is contained in:
elb
2025-02-21 11:11:27 -05:00
committed by GitHub
parent 438f80b911
commit 5455fd9772
5 changed files with 147 additions and 45 deletions

View File

@@ -146,14 +146,16 @@ func (gd *GameReader) getStatsList(statListPtr uintptr) stat.Stats {
}
var stats = make([]stat.Data, 0)
statBuffer := gd.Process.ReadBytesFromMemory(uintptr(statList), statCount*10)
for i := 0; i < int(statCount); i++ {
offset := uint(i * 8)
statLayer := ReadUIntFromBuffer(statBuffer, offset, Uint16)
statEnum := ReadUIntFromBuffer(statBuffer, offset+0x2, Uint16)
statValue := ReadUIntFromBuffer(statBuffer, offset+0x4, Uint32)
statValue := ReadIntFromBuffer(statBuffer, offset+0x4, Uint32)
value := int(statValue)
value := statValue
switch stat.ID(statEnum) {
case stat.Life,
stat.MaxLife,
@@ -161,16 +163,16 @@ func (gd *GameReader) getStatsList(statListPtr uintptr) stat.Stats {
stat.MaxMana,
stat.Stamina,
stat.MaxStamina:
value = int(statValue >> 8)
value = statValue >> 8
case stat.ColdLength,
stat.PoisonLength:
value = int(statValue / 25)
value = statValue / 25
case stat.DeadlyStrikePerLevel:
value = int(float64(statValue) / .8)
case stat.HitCausesMonsterToFlee:
value = int(float64(statValue) / 1.28)
case stat.AttackRatingUndeadPerLevel:
value = int(statValue / 2)
value = statValue / 2
case stat.MagicFindPerLevel,
stat.ExtraGoldPerLevel,
stat.DamageDemonPerLevel,

View File

@@ -182,7 +182,7 @@ func (gd *GameReader) Inventory(rawPlayerUnits RawPlayerUnits, hover data.HoverD
break
}
}
case item.QualityRare:
case item.QualityRare, item.QualityCrafted:
// Set item name from rare affixes
if prefix, exists := item.RarePrefixDesc[int(rarePrefix)]; exists {
if suffix, exists := item.RareSuffixDesc[int(rareSuffix)]; exists {
@@ -233,7 +233,7 @@ func (gd *GameReader) Inventory(rawPlayerUnits RawPlayerUnits, hover data.HoverD
if len(prefixParts) > 0 {
nameParts = append(nameParts, prefixParts...)
}
nameParts = append(nameParts, string(itm.Name))
nameParts = append(nameParts, itm.Desc().Name)
if len(suffixParts) > 0 {
nameParts = append(nameParts, suffixParts...)
}
@@ -436,7 +436,7 @@ func (gd *GameReader) Inventory(rawPlayerUnits RawPlayerUnits, hover data.HoverD
maxReq := calculateItemLevelReq(itm, baseDesc)
// Check magic/rare affixes
if itm.Identified && (itm.Quality == item.QualityMagic || itm.Quality == item.QualityRare) {
if itm.Identified && (itm.Quality == item.QualityMagic || itm.Quality == item.QualityRare || itm.Quality == item.QualityCrafted) {
maxReq = updateMaxReqFromAffixes(maxReq, itm.Affixes)
}
@@ -631,6 +631,43 @@ func calculateItemLevelReq(itm *data.Item, baseDesc item.Description) int {
// Start with base item's requirement
maxReq := baseDesc.RequiredLevel
// Handle Crafted items first as they have special calculation rules
if itm.Quality == item.QualityCrafted {
// Count number of affixes and find highest level requirement
affixCount := 0
maxAffixReq := 0
for _, prefixID := range itm.Affixes.Magic.Prefixes {
if prefix, exists := item.MagicPrefixDesc[int(prefixID)]; exists && prefixID != 0 {
affixCount++
if prefix.LevelReq > maxAffixReq {
maxAffixReq = prefix.LevelReq
}
}
}
for _, suffixID := range itm.Affixes.Magic.Suffixes {
if suffix, exists := item.MagicSuffixDesc[int(suffixID)]; exists && suffixID != 0 {
affixCount++
if suffix.LevelReq > maxAffixReq {
maxAffixReq = suffix.LevelReq
}
}
}
// Base calculation
levelReq := maxAffixReq + (3 * affixCount)
// Add 10 to final level req
levelReq += 10
// Cap at 98 if needed (Maximum possible)
if levelReq > 99 {
levelReq = 98
}
return levelReq
}
// Handle Unique/Set items and their special upgrade cases
if itm.Quality == item.QualityUnique || itm.Quality == item.QualitySet {
itemCode := baseDesc.Code
normalCode := baseDesc.NormalCode
@@ -655,6 +692,7 @@ func calculateItemLevelReq(itm *data.Item, baseDesc item.Description) int {
}
}
// Apply upgrade requirements
if itemCode == eliteCode {
if originalCode == normalCode {
// Double upgraded: Normal -> Exceptional -> Elite
@@ -679,7 +717,7 @@ func calculateItemLevelReq(itm *data.Item, baseDesc item.Description) int {
break
}
}
} else { // Set item
} else if itm.Quality == item.QualitySet {
for _, setItemInfo := range item.SetItems {
if setItemInfo.ID == int(itm.UniqueSetID) {
if setItemInfo.LevelReq > maxReq {
@@ -691,6 +729,5 @@ func calculateItemLevelReq(itm *data.Item, baseDesc item.Description) int {
}
}
}
return maxReq
}

View File

@@ -20,6 +20,13 @@ type Process struct {
moduleBaseSize uint32
}
const (
Int8 = 1 // signed 8-bit integer
Int16 = 2 // signed 16-bit integer
Int32 = 4 // signed 32-bit integer
Int64 = 8 // signed 64-bit integer
)
func NewProcess() (Process, error) {
module, err := getGameModule()
if err != nil {
@@ -144,6 +151,22 @@ func bytesToUint(bytes []byte, size IntType) uint {
return 0
}
func ReadIntFromBuffer(bytes []byte, offset uint, size IntType) int {
return bytesToInt(bytes[offset:offset+uint(size)], size)
}
func bytesToInt(bytes []byte, size IntType) int {
switch size {
case Int8:
return int(int8(bytes[0]))
case Int16:
return int(int16(binary.LittleEndian.Uint16(bytes)))
case Int32:
return int(int32(binary.LittleEndian.Uint32(bytes)))
case Int64:
return int(int64(binary.LittleEndian.Uint64(bytes)))
}
return 0
}
func (p Process) ReadStringFromMemory(address uintptr, size uint) string {
if size == 0 {