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:
@@ -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,
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user