improve 1

This commit is contained in:
vietdungdev
2026-05-25 11:20:22 +07:00
parent c4efbdfcf1
commit 1e3cd850d8

View File

@@ -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,35 +277,29 @@ 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 {
case "type":
stage1Props["type"] = it.Type().ID
case "quality":
stage1Props["quality"] = int(it.Quality)
case "class":
stage1Props["class"] = int(it.Desc().Tier())
case "name":
stage1Props["name"] = it.ID
case "flag":
// 0x400000 (eth) | 0x4000000 (runeword) kolbot style. Bit values are // 0x400000 (eth) | 0x4000000 (runeword) kolbot style. Bit values are
// shared with the rewriter (replaceStringPropertiesInStage1) via the // shared with the rewriter (replaceStringPropertiesInStage1) via the
// package-level flagBit* constants so the rewriter's enumeration of // package-level flagBit* constants so the rewriter's enumeration of
// possible flag-states stays in lock-step with the values written // possible flag-states stays in lock-step with the values written
// here — adding a third bit requires editing both this writer AND // here — adding a third bit requires editing both this writer AND
// the rewriter, but the constants make the dependency discoverable. // the rewriter, but the constants make the dependency discoverable.
*/
currentFlag := 0 currentFlag := 0
if it.Ethereal { if it.Ethereal {
currentFlag |= flagBitEthereal currentFlag |= flagBitEthereal
} }
if it.IsRuneword { if it.IsRuneword {
currentFlag |= flagBitRuneword currentFlag |= flagBitRuneword
} }
stage1Props := map[string]int{
stage1Props["flag"] = currentFlag "type": desc.GetType().ID,
case "prefix": "quality": int(it.Quality),
"class": int(desc.Tier()),
"name": it.ID,
"flag": currentFlag,
}
if it.Affixes.Rare.Prefix != 0 { if it.Affixes.Rare.Prefix != 0 {
stage1Props["prefix"] = int(it.Affixes.Rare.Prefix) stage1Props["prefix"] = int(it.Affixes.Rare.Prefix)
} }
@@ -296,7 +309,6 @@ func (r Rule) Evaluate(it data.Item) (RuleResult, error) {
break break
} }
} }
case "suffix":
if it.Affixes.Rare.Suffix != 0 { if it.Affixes.Rare.Suffix != 0 {
stage1Props["suffix"] = int(it.Affixes.Rare.Suffix) stage1Props["suffix"] = int(it.Affixes.Rare.Suffix)
} }
@@ -306,10 +318,6 @@ func (r Rule) Evaluate(it data.Item) (RuleResult, error) {
break break
} }
} }
case "color":
// TODO: Not supported yet
}
}
// Check if stage1 exists before evaluating // Check if stage1 exists before evaluating
if r.stage1 == nil { if r.stage1 == nil {
@@ -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
} }