Rewrite to Go: engine, plugin system, D2R plugin, API, loot filter
This commit is contained in:
parent
e0282a7111
commit
3b363192f2
60 changed files with 1576 additions and 3407 deletions
120
pkg/engine/loot/filter.go
Normal file
120
pkg/engine/loot/filter.go
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
// Package loot provides a declarative, rule-based loot filter engine.
|
||||
//
|
||||
// Loot rules are defined in YAML and evaluated against detected items.
|
||||
// Supports complex matching on type, name, rarity, properties, and more.
|
||||
package loot
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"git.cloonar.com/openclawd/iso-bot/pkg/plugin"
|
||||
)
|
||||
|
||||
// Priority levels for pickup ordering.
|
||||
const (
|
||||
PriorityLow = 1
|
||||
PriorityNormal = 5
|
||||
PriorityHigh = 8
|
||||
PriorityCritical = 10
|
||||
)
|
||||
|
||||
// Condition defines a match condition for a loot rule.
|
||||
type Condition struct {
|
||||
Type *string `yaml:"type,omitempty"` // "unique", "set", "rare", etc.
|
||||
NameContains *string `yaml:"name_contains,omitempty"` // substring match
|
||||
NameExact *string `yaml:"name_exact,omitempty"` // exact match
|
||||
MinRarity *int `yaml:"min_rarity,omitempty"` // minimum rarity tier
|
||||
MaxRarity *int `yaml:"max_rarity,omitempty"`
|
||||
BaseType *string `yaml:"base_type,omitempty"` // e.g., "Diadem", "Monarch"
|
||||
HasProperty *string `yaml:"has_property,omitempty"` // item has this property key
|
||||
PropertyGTE map[string]int `yaml:"property_gte,omitempty"` // property >= value
|
||||
}
|
||||
|
||||
// Action defines what to do when a rule matches.
|
||||
type Action string
|
||||
|
||||
const (
|
||||
ActionPickup Action = "pickup"
|
||||
ActionIgnore Action = "ignore"
|
||||
ActionAlert Action = "alert" // pickup + send notification
|
||||
)
|
||||
|
||||
// Rule is a single loot filter rule.
|
||||
type Rule struct {
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Match Condition `yaml:"match"`
|
||||
Action Action `yaml:"action"`
|
||||
Priority int `yaml:"priority,omitempty"`
|
||||
}
|
||||
|
||||
// RuleEngine evaluates items against a list of rules.
|
||||
type RuleEngine struct {
|
||||
Rules []Rule
|
||||
}
|
||||
|
||||
// NewRuleEngine creates a rule engine from a list of rules.
|
||||
func NewRuleEngine(rules []Rule) *RuleEngine {
|
||||
return &RuleEngine{Rules: rules}
|
||||
}
|
||||
|
||||
// Evaluate checks an item against all rules.
|
||||
// Returns the first matching rule's action and priority.
|
||||
func (e *RuleEngine) Evaluate(item plugin.DetectedItem) (action Action, priority int, matched bool) {
|
||||
for _, rule := range e.Rules {
|
||||
if e.matches(rule.Match, item) {
|
||||
p := rule.Priority
|
||||
if p == 0 {
|
||||
p = PriorityNormal
|
||||
}
|
||||
return rule.Action, p, true
|
||||
}
|
||||
}
|
||||
return ActionIgnore, 0, false
|
||||
}
|
||||
|
||||
// ShouldPickup implements plugin.LootFilter.
|
||||
func (e *RuleEngine) ShouldPickup(item plugin.DetectedItem) (bool, int) {
|
||||
action, priority, matched := e.Evaluate(item)
|
||||
if !matched {
|
||||
return false, 0
|
||||
}
|
||||
return action == ActionPickup || action == ActionAlert, priority
|
||||
}
|
||||
|
||||
// ShouldAlert implements plugin.LootFilter.
|
||||
func (e *RuleEngine) ShouldAlert(item plugin.DetectedItem) bool {
|
||||
action, _, matched := e.Evaluate(item)
|
||||
return matched && action == ActionAlert
|
||||
}
|
||||
|
||||
func (e *RuleEngine) matches(cond Condition, item plugin.DetectedItem) bool {
|
||||
if cond.Type != nil && !strings.EqualFold(item.Type, *cond.Type) {
|
||||
return false
|
||||
}
|
||||
if cond.NameContains != nil && !strings.Contains(strings.ToLower(item.Name), strings.ToLower(*cond.NameContains)) {
|
||||
return false
|
||||
}
|
||||
if cond.NameExact != nil && !strings.EqualFold(item.Name, *cond.NameExact) {
|
||||
return false
|
||||
}
|
||||
if cond.MinRarity != nil && item.Rarity < *cond.MinRarity {
|
||||
return false
|
||||
}
|
||||
if cond.MaxRarity != nil && item.Rarity > *cond.MaxRarity {
|
||||
return false
|
||||
}
|
||||
if cond.BaseType != nil && !strings.EqualFold(item.Properties["base_type"], *cond.BaseType) {
|
||||
return false
|
||||
}
|
||||
if cond.HasProperty != nil {
|
||||
if _, ok := item.Properties[*cond.HasProperty]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
for key, minVal := range cond.PropertyGTE {
|
||||
// TODO: Parse property value as int and compare
|
||||
_ = key
|
||||
_ = minVal
|
||||
}
|
||||
return true
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue