improve 1
This commit is contained in:
151
pkg/nip/rule.go
151
pkg/nip/rule.go
@@ -25,6 +25,7 @@ var (
|
|||||||
maxQtyRegexp = regexp.MustCompile(`(\[maxquantity]\s*(<=|<|>|>=|!=|==)\s*([0-9]+))`)
|
maxQtyRegexp = regexp.MustCompile(`(\[maxquantity]\s*(<=|<|>|>=|!=|==)\s*([0-9]+))`)
|
||||||
tierRegexp = regexp.MustCompile(`(\[tier]\s*(<=|<|>|>=|!=|==)\s*([0-9]+))`)
|
tierRegexp = regexp.MustCompile(`(\[tier]\s*(<=|<|>|>=|!=|==)\s*([0-9]+))`)
|
||||||
mercTierRegexp = regexp.MustCompile(`(\[merctier]\s*(<=|<|>|>=|!=|==)\s*([0-9]+))`)
|
mercTierRegexp = regexp.MustCompile(`(\[merctier]\s*(<=|<|>|>=|!=|==)\s*([0-9]+))`)
|
||||||
|
whitespaceRegexp = regexp.MustCompile(`\s+`)
|
||||||
)
|
)
|
||||||
|
|
||||||
type Rule struct {
|
type Rule struct {
|
||||||
@@ -38,6 +39,10 @@ type Rule struct {
|
|||||||
stage1 *vm.Program
|
stage1 *vm.Program
|
||||||
stage2 *vm.Program
|
stage2 *vm.Program
|
||||||
requiredStats []string
|
requiredStats []string
|
||||||
|
skillTabSum bool
|
||||||
|
classSkillSum bool
|
||||||
|
resistSum bool
|
||||||
|
nonResistStat bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleResult int
|
type RuleResult int
|
||||||
@@ -188,7 +193,7 @@ func NewRule(rawRule string, filename string, lineNumber int) (Rule, error) {
|
|||||||
mercTier: mercTier,
|
mercTier: mercTier,
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.Split(rule, "#")
|
parts := strings.SplitN(rule, "#", 3)
|
||||||
|
|
||||||
if len(parts) > 0 {
|
if len(parts) > 0 {
|
||||||
stage1 := strings.TrimSpace(parts[0])
|
stage1 := strings.TrimSpace(parts[0])
|
||||||
@@ -213,10 +218,24 @@ func NewRule(rawRule string, filename string, lineNumber int) (Rule, error) {
|
|||||||
if stage2 != "" {
|
if stage2 != "" {
|
||||||
// Extract stats before removing brackets for compilation
|
// Extract stats before removing brackets for compilation
|
||||||
r.requiredStats = getRequiredStatsForRule(stage2)
|
r.requiredStats = getRequiredStatsForRule(stage2)
|
||||||
|
stage2Source := ""
|
||||||
|
rawParts := strings.SplitN(rawRule, "#", 3)
|
||||||
|
if len(rawParts) > 1 {
|
||||||
|
stage2Source = strings.ToLower(rawParts[1])
|
||||||
|
}
|
||||||
|
r.skillTabSum = strings.Contains(stage2Source, "[itemaddskilltab]")
|
||||||
|
r.classSkillSum = strings.Contains(stage2Source, "[itemaddclassskills]")
|
||||||
|
if strings.Contains(stage2Source, "resist") {
|
||||||
|
r.resistSum = strings.Contains(stage2Source, "+") || strings.Contains(stage2Source, "-") ||
|
||||||
|
(strings.Contains(stage2Source, "(") && strings.Contains(stage2Source, ")"))
|
||||||
|
}
|
||||||
|
|
||||||
statsMap := make(map[string]int)
|
statsMap := make(map[string]int)
|
||||||
for _, prop := range r.requiredStats {
|
for _, prop := range r.requiredStats {
|
||||||
statsMap[prop] = 0
|
statsMap[prop] = 0
|
||||||
|
if !strings.Contains(prop, "resist") {
|
||||||
|
r.nonResistStat = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize whitespace around operators in parenthesized expressions
|
// Normalize whitespace around operators in parenthesized expressions
|
||||||
@@ -247,7 +266,7 @@ func normalizeParenthesizedExpressions(expr string) string {
|
|||||||
expr = strings.ReplaceAll(expr, "<=", " <= ")
|
expr = strings.ReplaceAll(expr, "<=", " <= ")
|
||||||
|
|
||||||
// Fix extra spaces
|
// Fix extra spaces
|
||||||
expr = regexp.MustCompile(`\s+`).ReplaceAllString(expr, " ")
|
expr = whitespaceRegexp.ReplaceAllString(expr, " ")
|
||||||
|
|
||||||
// Normalize parentheses spacing
|
// Normalize parentheses spacing
|
||||||
expr = strings.ReplaceAll(expr, "( ", "(")
|
expr = strings.ReplaceAll(expr, "( ", "(")
|
||||||
@@ -258,56 +277,45 @@ func normalizeParenthesizedExpressions(expr string) string {
|
|||||||
|
|
||||||
func (r Rule) Evaluate(it data.Item) (RuleResult, error) {
|
func (r Rule) Evaluate(it data.Item) (RuleResult, error) {
|
||||||
// Stage 1: Basic properties evaluation
|
// Stage 1: Basic properties evaluation
|
||||||
stage1Props := make(map[string]int)
|
desc := it.Desc()
|
||||||
for prop := range fixedPropsList {
|
/*
|
||||||
switch prop {
|
// 0x400000 (eth) | 0x4000000 (runeword) kolbot style. Bit values are
|
||||||
case "type":
|
// shared with the rewriter (replaceStringPropertiesInStage1) via the
|
||||||
stage1Props["type"] = it.Type().ID
|
// package-level flagBit* constants so the rewriter's enumeration of
|
||||||
case "quality":
|
// possible flag-states stays in lock-step with the values written
|
||||||
stage1Props["quality"] = int(it.Quality)
|
// here — adding a third bit requires editing both this writer AND
|
||||||
case "class":
|
// the rewriter, but the constants make the dependency discoverable.
|
||||||
stage1Props["class"] = int(it.Desc().Tier())
|
*/
|
||||||
case "name":
|
currentFlag := 0
|
||||||
stage1Props["name"] = it.ID
|
if it.Ethereal {
|
||||||
case "flag":
|
currentFlag |= flagBitEthereal
|
||||||
// 0x400000 (eth) | 0x4000000 (runeword) kolbot style. Bit values are
|
}
|
||||||
// shared with the rewriter (replaceStringPropertiesInStage1) via the
|
if it.IsRuneword {
|
||||||
// package-level flagBit* constants so the rewriter's enumeration of
|
currentFlag |= flagBitRuneword
|
||||||
// possible flag-states stays in lock-step with the values written
|
}
|
||||||
// here — adding a third bit requires editing both this writer AND
|
stage1Props := map[string]int{
|
||||||
// the rewriter, but the constants make the dependency discoverable.
|
"type": desc.GetType().ID,
|
||||||
currentFlag := 0
|
"quality": int(it.Quality),
|
||||||
|
"class": int(desc.Tier()),
|
||||||
if it.Ethereal {
|
"name": it.ID,
|
||||||
currentFlag |= flagBitEthereal
|
"flag": currentFlag,
|
||||||
}
|
}
|
||||||
if it.IsRuneword {
|
if it.Affixes.Rare.Prefix != 0 {
|
||||||
currentFlag |= flagBitRuneword
|
stage1Props["prefix"] = int(it.Affixes.Rare.Prefix)
|
||||||
}
|
}
|
||||||
|
for _, prefix := range it.Affixes.Magic.Prefixes {
|
||||||
stage1Props["flag"] = currentFlag
|
if prefix != 0 {
|
||||||
case "prefix":
|
stage1Props["prefix"] = int(prefix)
|
||||||
if it.Affixes.Rare.Prefix != 0 {
|
break
|
||||||
stage1Props["prefix"] = int(it.Affixes.Rare.Prefix)
|
}
|
||||||
}
|
}
|
||||||
for _, prefix := range it.Affixes.Magic.Prefixes {
|
if it.Affixes.Rare.Suffix != 0 {
|
||||||
if prefix != 0 {
|
stage1Props["suffix"] = int(it.Affixes.Rare.Suffix)
|
||||||
stage1Props["prefix"] = int(prefix)
|
}
|
||||||
break
|
for _, suffix := range it.Affixes.Magic.Suffixes {
|
||||||
}
|
if suffix != 0 {
|
||||||
}
|
stage1Props["suffix"] = int(suffix)
|
||||||
case "suffix":
|
break
|
||||||
if it.Affixes.Rare.Suffix != 0 {
|
|
||||||
stage1Props["suffix"] = int(it.Affixes.Rare.Suffix)
|
|
||||||
}
|
|
||||||
for _, suffix := range it.Affixes.Magic.Suffixes {
|
|
||||||
if suffix != 0 {
|
|
||||||
stage1Props["suffix"] = int(suffix)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case "color":
|
|
||||||
// TODO: Not supported yet
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -337,51 +345,22 @@ func (r Rule) Evaluate(it data.Item) (RuleResult, error) {
|
|||||||
return RuleResultPartial, nil
|
return RuleResultPartial, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
stage2Props := make(map[string]int)
|
stage2Props := make(map[string]int, len(r.requiredStats))
|
||||||
stage2 := ""
|
|
||||||
if len(strings.Split(r.RawLine, "#")) > 1 {
|
|
||||||
stage2 = strings.ToLower(strings.Split(r.RawLine, "#")[1])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special handling for skill tabs
|
// Special handling for skill tabs
|
||||||
if strings.Contains(stage2, "[itemaddskilltab]") {
|
if r.skillTabSum {
|
||||||
stage2Props["itemaddskilltab"] = evaluateSkillTabSum(it)
|
stage2Props["itemaddskilltab"] = evaluateSkillTabSum(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Special handling for class skills
|
// Special handling for class skills
|
||||||
if strings.Contains(stage2, "[itemaddclassskills]") {
|
if r.classSkillSum {
|
||||||
stage2Props["itemaddclassskills"] = evaluateClassSkillsSum(it)
|
stage2Props["itemaddclassskills"] = evaluateClassSkillsSum(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Preprocess stage2 to see if certain stats are being compared to zero
|
|
||||||
zeroCheckStats := make(map[string]bool)
|
|
||||||
for _, statName := range r.requiredStats {
|
|
||||||
if strings.Contains(stage2, "["+statName+"] == 0") ||
|
|
||||||
strings.Contains(stage2, "["+statName+"]==0") {
|
|
||||||
zeroCheckStats[statName] = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle resist sums - check if item has any resist stats (including negative values)
|
// Handle resist sums - check if item has any resist stats (including negative values)
|
||||||
hasAnyResist := false
|
hasAnyResist := false
|
||||||
|
|
||||||
// Detect if this is a rule with a resist sum expression
|
if r.resistSum {
|
||||||
isResistSum := false
|
|
||||||
if strings.Contains(stage2, "resist") {
|
|
||||||
isResistSum = strings.Contains(stage2, "+") || strings.Contains(stage2, "-") ||
|
|
||||||
(strings.Contains(stage2, "(") && strings.Contains(stage2, ")"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if rule ONLY checks resist sums (no other OR conditions)
|
|
||||||
hasNonResistConditions := false
|
|
||||||
for _, statName := range r.requiredStats {
|
|
||||||
if !strings.Contains(statName, "resist") {
|
|
||||||
hasNonResistConditions = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isResistSum {
|
|
||||||
// Check if the item has any resist stats at all (including negative values like sunders)
|
// Check if the item has any resist stats at all (including negative values like sunders)
|
||||||
for _, statName := range r.requiredStats {
|
for _, statName := range r.requiredStats {
|
||||||
if !strings.Contains(statName, "resist") {
|
if !strings.Contains(statName, "resist") {
|
||||||
@@ -474,7 +453,7 @@ func (r Rule) Evaluate(it data.Item) (RuleResult, error) {
|
|||||||
isResistStat := strings.Contains(statName, "resist")
|
isResistStat := strings.Contains(statName, "resist")
|
||||||
// Special case: Pure resist-sum rules (like sunders) should fail early if no resists exist
|
// Special case: Pure resist-sum rules (like sunders) should fail early if no resists exist
|
||||||
// But mixed rules (resists OR other stats) should continue to check other conditions
|
// But mixed rules (resists OR other stats) should continue to check other conditions
|
||||||
if isResistStat && isResistSum && !hasAnyResist && !hasNonResistConditions {
|
if isResistStat && r.resistSum && !hasAnyResist && !r.nonResistStat {
|
||||||
// This is a pure resist rule and item has no resists - can't match
|
// This is a pure resist rule and item has no resists - can't match
|
||||||
return RuleResultNoMatch, nil
|
return RuleResultNoMatch, nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user