Merge remote-tracking branch 'origin/main' into pr/CarlPoppa1/9
This commit is contained in:
@@ -66,6 +66,7 @@ type OnlineGame struct {
|
|||||||
LastGameName string
|
LastGameName string
|
||||||
LastGamePassword string
|
LastGamePassword string
|
||||||
FPS int
|
FPS int
|
||||||
|
Ping int
|
||||||
}
|
}
|
||||||
|
|
||||||
type Panel struct {
|
type Panel struct {
|
||||||
|
|||||||
@@ -2,5 +2,4 @@ package memory
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
tzOffline = 0x29B3208
|
tzOffline = 0x29B3208
|
||||||
tzOnline = 0x29B3188
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ func (gd *GameReader) GetData() data.Data {
|
|||||||
LastGameName: gd.LastGameName(),
|
LastGameName: gd.LastGameName(),
|
||||||
LastGamePassword: gd.LastGamePass(),
|
LastGamePassword: gd.LastGamePass(),
|
||||||
FPS: gd.FPS(),
|
FPS: gd.FPS(),
|
||||||
|
Ping: gd.Ping(),
|
||||||
},
|
},
|
||||||
Monsters: monsters,
|
Monsters: monsters,
|
||||||
Corpses: gd.Corpses(pu.Position, hover),
|
Corpses: gd.Corpses(pu.Position, hover),
|
||||||
@@ -312,7 +313,7 @@ func (gd *GameReader) GetSelectedCharacterName() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) LegacyGraphics() bool {
|
func (gd *GameReader) LegacyGraphics() bool {
|
||||||
return gd.ReadUInt(gd.Process.moduleBaseAddressPtr+0x22D845F, Uint8) != 0
|
return gd.ReadUInt(gd.Process.moduleBaseAddressPtr+gd.offset.LegacyGraphics, Uint8) != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) IsOnline() bool {
|
func (gd *GameReader) IsOnline() bool {
|
||||||
@@ -428,17 +429,23 @@ func (gd *GameReader) IsDismissableModalPresent() (bool, string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) LastGameName() string {
|
func (gd *GameReader) LastGameName() string {
|
||||||
return gd.ReadStringFromMemory(gd.moduleBaseAddressPtr+0x2587FB8, 0)
|
return gd.ReadStringFromMemory(gd.moduleBaseAddressPtr+0x256E668, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) LastGamePass() string {
|
func (gd *GameReader) LastGamePass() string {
|
||||||
return gd.ReadStringFromMemory(gd.moduleBaseAddressPtr+0x2588018, 0)
|
return gd.ReadStringFromMemory(gd.moduleBaseAddressPtr+0x256E6C8, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (gd *GameReader) FPS() int {
|
func (gd *GameReader) FPS() int {
|
||||||
return int(gd.ReadUInt(gd.moduleBaseAddressPtr+gd.offset.FPS, Uint32))
|
return int(gd.ReadUInt(gd.moduleBaseAddressPtr+gd.offset.FPS, Uint32))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (gd *GameReader) Ping() int {
|
||||||
|
ptrToStructPtr := gd.moduleBaseAddressPtr + gd.offset.Ping
|
||||||
|
structPtrAddr := gd.ReadUInt(ptrToStructPtr, Uint64)
|
||||||
|
return int(gd.ReadUInt(uintptr(structPtrAddr+36), Uint32))
|
||||||
|
}
|
||||||
|
|
||||||
func (gd *GameReader) HasMerc() bool {
|
func (gd *GameReader) HasMerc() bool {
|
||||||
return gd.ReadUInt(gd.Process.moduleBaseAddressPtr+gd.offset.UI+0x8, Uint8) != 0
|
return gd.ReadUInt(gd.Process.moduleBaseAddressPtr+gd.offset.UI+0x8, Uint8) != 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (gd *GameReader) GetKeyBindings() data.KeyBindings {
|
func (gd *GameReader) GetKeyBindings() data.KeyBindings {
|
||||||
blob := gd.ReadBytesFromMemory(gd.moduleBaseAddressPtr+0x1DE5C24, 0x500)
|
blob := gd.ReadBytesFromMemory(gd.moduleBaseAddressPtr+gd.offset.KeyBindingsOffset, 0x500)
|
||||||
blobSkills := gd.ReadBytesFromMemory(gd.offset.KeyBindingsSkillsOffset, 0x500)
|
blobSkills := gd.ReadBytesFromMemory(gd.offset.KeyBindingsSkillsOffset, 0x500)
|
||||||
|
|
||||||
skillsKB := [16]data.SkillBinding{}
|
skillsKB := [16]data.SkillBinding{}
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ type Offset struct {
|
|||||||
KeyBindingsOffset uintptr
|
KeyBindingsOffset uintptr
|
||||||
KeyBindingsSkillsOffset uintptr
|
KeyBindingsSkillsOffset uintptr
|
||||||
QuestInfo uintptr
|
QuestInfo uintptr
|
||||||
|
TZ uintptr
|
||||||
|
Quests uintptr
|
||||||
|
Ping uintptr
|
||||||
|
LegacyGraphics uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculateOffsets(process *Process) Offset {
|
func calculateOffsets(process *Process) Offset {
|
||||||
@@ -75,14 +79,16 @@ func calculateOffsets(process *Process) Offset {
|
|||||||
fpsOffset := pattern - process.moduleBaseAddressPtr + 6 + fpsOffsetPtr
|
fpsOffset := pattern - process.moduleBaseAddressPtr + 6 + fpsOffsetPtr
|
||||||
|
|
||||||
// Keybindings
|
// Keybindings
|
||||||
pattern = process.FindPattern(memory, "\x00\xA8\x00\x00\x00\x00\x7F\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x0F\x00\x00\x00\x00\x00\x00\x80\x4C\x6F\x00\x00\x79\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x52", "xx????x?xx????xxxxxxxxxxxxx??x?xxxxxxxxxxx")
|
pattern = process.FindPattern(memory, "\x48\x8D\x05\xAF\xEE", "xxxxx")
|
||||||
keyBindingsOffset := pattern + 0x65
|
bytes = process.ReadBytesFromMemory(pattern+3, 4)
|
||||||
|
relativeOffset := int32(binary.LittleEndian.Uint32(bytes))
|
||||||
|
keyBindingsOffset := pattern - process.moduleBaseAddressPtr + 7 + uintptr(relativeOffset)
|
||||||
|
|
||||||
// KeyBindings Skills
|
// KeyBindings Skills
|
||||||
pattern = process.FindPattern(memory, "\x0F\x10\x04\x24\x48\x6B\xC8\x1C\x48\x8D\x05", "xxxxxxxxxxx")
|
pattern = process.FindPattern(memory, "\x0F\x10\x04\x24\x48\x6B\xC8\x1C\x48\x8D\x05", "xxxxxxxxxxx")
|
||||||
var keyBindingsSkillsOffset uintptr
|
var keyBindingsSkillsOffset uintptr
|
||||||
bytes = process.ReadBytesFromMemory(pattern+11, 4)
|
bytes = process.ReadBytesFromMemory(pattern+11, 4)
|
||||||
relativeOffset := int32(binary.LittleEndian.Uint32(bytes))
|
relativeOffset = int32(binary.LittleEndian.Uint32(bytes))
|
||||||
keyBindingsSkillsOffset = uintptr(int64(pattern) + 15 + int64(relativeOffset))
|
keyBindingsSkillsOffset = uintptr(int64(pattern) + 15 + int64(relativeOffset))
|
||||||
|
|
||||||
// QuestInfo
|
// QuestInfo
|
||||||
@@ -90,6 +96,28 @@ func calculateOffsets(process *Process) Offset {
|
|||||||
questInfoOffsetPtr := uintptr(process.ReadUInt(pattern+3, Uint32))
|
questInfoOffsetPtr := uintptr(process.ReadUInt(pattern+3, Uint32))
|
||||||
questInfoOffset := pattern - process.moduleBaseAddressPtr + 7 + questInfoOffsetPtr
|
questInfoOffset := pattern - process.moduleBaseAddressPtr + 7 + questInfoOffsetPtr
|
||||||
|
|
||||||
|
// Terror Zones
|
||||||
|
pattern = process.FindPattern(memory, "\x48\x89\x05\xCC\xCC\xCC\xCC\x48\x8D\x05\xCC\xCC\xCC\xCC\x48\x89\x05\xCC\xCC\xCC\xCC\x48\x8D\x05\xCC\xCC\xCC\xCC\x48\x89\x15\xCC\xCC\xCC\xCC\x48\x89\x15", "xxx????xxx????xxx????xxx????xxx????xxx")
|
||||||
|
tzPtr := process.ReadUInt(pattern+3, Uint32)
|
||||||
|
tzOffset := pattern - process.moduleBaseAddressPtr + 7 + uintptr(tzPtr)
|
||||||
|
|
||||||
|
// Quest Bytes Data
|
||||||
|
pattern = process.FindPattern(memory, "\x42\xc6\x84\x28\x00\x00\x00\x00\x00\x49\xff\xc5\x49\x83\xfd\x29", "xxxx?????xxxxxxx")
|
||||||
|
bytes = process.ReadBytesFromMemory(pattern+4, 4)
|
||||||
|
questOffset := uintptr(binary.LittleEndian.Uint32(bytes))
|
||||||
|
questDataOffset := questOffset + 1
|
||||||
|
|
||||||
|
// Ping
|
||||||
|
pattern = process.FindPattern(memory, "\x48\x8B\x0D\xCC\xCC\xCC\xCC\x49\x2B\xC7", "xxx????xxx")
|
||||||
|
bytes = process.ReadBytesFromMemory(pattern+3, 4)
|
||||||
|
relativeOffset = int32(binary.LittleEndian.Uint32(bytes))
|
||||||
|
pingOffset := pattern - process.moduleBaseAddressPtr + 7 + uintptr(relativeOffset)
|
||||||
|
|
||||||
|
// LegacyGraphics
|
||||||
|
pattern = process.FindPattern(memory, "\x80\x3D\x00\x00\x00\x00\x00\x48\x8D\x54\x24\x30", "xx?????xxxxx")
|
||||||
|
legacyGfxPtr := uintptr(process.ReadUInt(pattern+2, Uint32))
|
||||||
|
legacyGfxOffset := pattern - process.moduleBaseAddressPtr + 7 + legacyGfxPtr
|
||||||
|
|
||||||
return Offset{
|
return Offset{
|
||||||
GameData: gameDataOffset,
|
GameData: gameDataOffset,
|
||||||
UnitTable: unitTableOffset,
|
UnitTable: unitTableOffset,
|
||||||
@@ -104,5 +132,9 @@ func calculateOffsets(process *Process) Offset {
|
|||||||
KeyBindingsOffset: keyBindingsOffset,
|
KeyBindingsOffset: keyBindingsOffset,
|
||||||
KeyBindingsSkillsOffset: keyBindingsSkillsOffset,
|
KeyBindingsSkillsOffset: keyBindingsSkillsOffset,
|
||||||
QuestInfo: questInfoOffset,
|
QuestInfo: questInfoOffset,
|
||||||
|
TZ: tzOffset,
|
||||||
|
Quests: questDataOffset,
|
||||||
|
Ping: pingOffset,
|
||||||
|
LegacyGraphics: legacyGfxOffset,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -133,11 +133,21 @@ func (gd *GameReader) getSkills(skillListPtr uintptr) map[skill.ID]skill.Points
|
|||||||
quantity := gd.Process.ReadUInt(skillPtr+0x40, Uint16)
|
quantity := gd.Process.ReadUInt(skillPtr+0x40, Uint16)
|
||||||
charges := gd.Process.ReadUInt(skillPtr+0x48, Uint16)
|
charges := gd.Process.ReadUInt(skillPtr+0x48, Uint16)
|
||||||
|
|
||||||
|
shouldSetSkill := true
|
||||||
|
existingSkill, exists := skills[skill.ID(skillTxt)]
|
||||||
|
if exists {
|
||||||
|
if existingSkill.Charges == 0 && charges > 0 {
|
||||||
|
shouldSetSkill = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if shouldSetSkill {
|
||||||
skills[skill.ID(skillTxt)] = skill.Points{
|
skills[skill.ID(skillTxt)] = skill.Points{
|
||||||
Level: lvl,
|
Level: lvl,
|
||||||
Quantity: quantity,
|
Quantity: quantity,
|
||||||
Charges: charges,
|
Charges: charges,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
skillPtr = uintptr(gd.Process.ReadUInt(skillPtr+0x08, Uint64))
|
skillPtr = uintptr(gd.Process.ReadUInt(skillPtr+0x08, Uint64))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (gd *GameReader) TerrorZones() (areas []area.ID) {
|
func (gd *GameReader) TerrorZones() (areas []area.ID) {
|
||||||
tz := gd.moduleBaseAddressPtr + tzOnline
|
structPtr := gd.moduleBaseAddressPtr + gd.offset.TZ
|
||||||
|
zonesPtr := uintptr(gd.ReadUInt(structPtr, Uint64))
|
||||||
|
actualActiveZoneCount := int(gd.ReadUInt(structPtr+0x8, Uint8))
|
||||||
|
|
||||||
for i := 0; i < 8; i++ {
|
for i := 0; i < actualActiveZoneCount; i++ {
|
||||||
tzArea := gd.ReadUInt(tz+uintptr(i*Uint32), Uint32)
|
tzArea := gd.ReadUInt(zonesPtr+uintptr(i*Uint32), Uint32)
|
||||||
if tzArea != 0 {
|
if tzArea != 0 {
|
||||||
areas = append(areas, area.ID(tzArea))
|
areas = append(areas, area.ID(tzArea))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ var (
|
|||||||
fixedPropsRegexp = regexp.MustCompile(`(\[(type|quality|class|name|flag|color|prefix|suffix)]\s*(<=|<|>|>=|!=|==)\s*([a-zA-Z0-9]+))`)
|
fixedPropsRegexp = regexp.MustCompile(`(\[(type|quality|class|name|flag|color|prefix|suffix)]\s*(<=|<|>|>=|!=|==)\s*([a-zA-Z0-9]+))`)
|
||||||
statsRegexp = regexp.MustCompile(`\[(.*?)]`)
|
statsRegexp = regexp.MustCompile(`\[(.*?)]`)
|
||||||
maxQtyRegexp = regexp.MustCompile(`(\[maxquantity]\s*(<=|<|>|>=|!=|==)\s*([0-9]+))`)
|
maxQtyRegexp = regexp.MustCompile(`(\[maxquantity]\s*(<=|<|>|>=|!=|==)\s*([0-9]+))`)
|
||||||
|
tierRegexp = regexp.MustCompile(`(\[tier]\s*(<=|<|>|>=|!=|==)\s*([0-9]+))`)
|
||||||
|
mercTierRegexp = regexp.MustCompile(`(\[merctier]\s*(<=|<|>|>=|!=|==)\s*([0-9]+))`)
|
||||||
)
|
)
|
||||||
|
|
||||||
type Rule struct {
|
type Rule struct {
|
||||||
@@ -31,6 +33,8 @@ type Rule struct {
|
|||||||
LineNumber int
|
LineNumber int
|
||||||
Enabled bool
|
Enabled bool
|
||||||
maxQuantity int
|
maxQuantity int
|
||||||
|
tier float64
|
||||||
|
mercTier float64
|
||||||
stage1 *vm.Program
|
stage1 *vm.Program
|
||||||
stage2 *vm.Program
|
stage2 *vm.Program
|
||||||
requiredStats []string
|
requiredStats []string
|
||||||
@@ -61,6 +65,57 @@ func (r Rules) EvaluateAll(it data.Item) (Rule, RuleResult) {
|
|||||||
return bestMatchingRule, bestMatch
|
return bestMatchingRule, bestMatch
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r Rules) EvaluateAllIgnoreTiers(it data.Item) (Rule, RuleResult) {
|
||||||
|
bestMatch := RuleResultNoMatch
|
||||||
|
bestMatchingRule := Rule{}
|
||||||
|
for _, rule := range r {
|
||||||
|
if rule.Enabled {
|
||||||
|
if rule.tier > 0 || rule.mercTier > 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result, err := rule.Evaluate(it)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if result == RuleResultFullMatch {
|
||||||
|
return rule, result
|
||||||
|
}
|
||||||
|
if result == RuleResultPartial {
|
||||||
|
bestMatch = result
|
||||||
|
bestMatchingRule = rule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestMatchingRule, bestMatch
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Rules) EvaluateTiers(it data.Item, tierRulesIndexes []int) (Rule, Rule) {
|
||||||
|
highestTierRule := Rule{}
|
||||||
|
highestMercTierRule := Rule{}
|
||||||
|
for _, ruleIndex := range tierRulesIndexes {
|
||||||
|
if ruleIndex < len(r) {
|
||||||
|
rule := r[ruleIndex]
|
||||||
|
if rule.Enabled {
|
||||||
|
result, err := rule.Evaluate(it)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if result == RuleResultFullMatch || result == RuleResultPartial {
|
||||||
|
if rule.tier > highestTierRule.tier {
|
||||||
|
highestTierRule = rule
|
||||||
|
}
|
||||||
|
if rule.mercTier > highestMercTierRule.mercTier {
|
||||||
|
highestMercTierRule = rule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return highestTierRule, highestMercTierRule
|
||||||
|
}
|
||||||
|
|
||||||
var fixedPropsList = map[string]int{"type": 0, "quality": 0, "class": 0, "name": 0, "flag": 0, "color": 0, "prefix": 0, "suffix": 0}
|
var fixedPropsList = map[string]int{"type": 0, "quality": 0, "class": 0, "name": 0, "flag": 0, "color": 0, "prefix": 0, "suffix": 0}
|
||||||
|
|
||||||
func NewRule(rawRule string, filename string, lineNumber int) (Rule, error) {
|
func NewRule(rawRule string, filename string, lineNumber int) (Rule, error) {
|
||||||
@@ -77,6 +132,28 @@ func NewRule(rawRule string, filename string, lineNumber int) (Rule, error) {
|
|||||||
rule = strings.ReplaceAll(rule, prop[0], "")
|
rule = strings.ReplaceAll(rule, prop[0], "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Try to get the tier value and purge it from the rule, we can not evaluate it yet
|
||||||
|
tier := 0.0
|
||||||
|
for _, prop := range tierRegexp.FindAllStringSubmatch(rule, -1) {
|
||||||
|
parsedTier, err := strconv.Atoi(prop[3])
|
||||||
|
if err != nil {
|
||||||
|
return Rule{}, fmt.Errorf("error parsing tier value %s: %w", prop[3], err)
|
||||||
|
}
|
||||||
|
tier = float64(parsedTier)
|
||||||
|
rule = strings.ReplaceAll(rule, prop[0], "")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get the merctier value and purge it from the rule, we can not evaluate it yet
|
||||||
|
mercTier := 0.0
|
||||||
|
for _, prop := range mercTierRegexp.FindAllStringSubmatch(rule, -1) {
|
||||||
|
parsedMercTier, err := strconv.Atoi(prop[3])
|
||||||
|
if err != nil {
|
||||||
|
return Rule{}, fmt.Errorf("error parsing merctier value %s: %w", prop[3], err)
|
||||||
|
}
|
||||||
|
mercTier = float64(parsedMercTier)
|
||||||
|
rule = strings.ReplaceAll(rule, prop[0], "")
|
||||||
|
}
|
||||||
|
|
||||||
// Sanitize again, just in case we messed up the rule while parsing maxquantity
|
// Sanitize again, just in case we messed up the rule while parsing maxquantity
|
||||||
rule = sanitizeLine(rule)
|
rule = sanitizeLine(rule)
|
||||||
if rule == "" {
|
if rule == "" {
|
||||||
@@ -89,6 +166,8 @@ func NewRule(rawRule string, filename string, lineNumber int) (Rule, error) {
|
|||||||
LineNumber: lineNumber,
|
LineNumber: lineNumber,
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
maxQuantity: maxQuantity,
|
maxQuantity: maxQuantity,
|
||||||
|
tier: tier,
|
||||||
|
mercTier: mercTier,
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.Split(rule, "#")
|
parts := strings.Split(rule, "#")
|
||||||
@@ -372,3 +451,11 @@ func getRequiredStatsForRule(line string) []string {
|
|||||||
func (r Rule) MaxQuantity() int {
|
func (r Rule) MaxQuantity() int {
|
||||||
return r.maxQuantity
|
return r.maxQuantity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r Rule) Tier() float64 {
|
||||||
|
return r.tier
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Rule) MercTier() float64 {
|
||||||
|
return r.mercTier
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user