diff --git a/pkg/conditional/conditional.go b/pkg/conditional/conditional.go index f6180c5c35..84dd38914d 100644 --- a/pkg/conditional/conditional.go +++ b/pkg/conditional/conditional.go @@ -46,6 +46,11 @@ func Eval(c *core.Core, fields []string) (any, error) { if key, ok := shortcut.CharNameToKey[name]; ok { return evalCharacter(c, key, fields) } - return 0, fmt.Errorf("invalid character %v in character condition", name) + + if trg, err := parseTarget(c, name); err == nil { + return evalTarget(c, trg, fields) + } + + return 0, fmt.Errorf("invalid token %v in condition", name) } } diff --git a/pkg/conditional/enemy.go b/pkg/conditional/enemy.go index 15a0dd3fc1..d33edc8f04 100644 --- a/pkg/conditional/enemy.go +++ b/pkg/conditional/enemy.go @@ -2,8 +2,9 @@ package conditional import ( "fmt" + "log" + "regexp" "strconv" - "strings" "github.com/genshinsim/gcsim/pkg/core" "github.com/genshinsim/gcsim/pkg/core/attributes" @@ -12,7 +13,35 @@ import ( "github.com/genshinsim/gcsim/pkg/reactable" ) -func evalDebuff(c *core.Core, fields []string) (bool, error) { +func evalTarget(c *core.Core, trg *enemy.Enemy, fields []string) (any, error) { + typ := fields[1] + switch typ { + case "mods", "status": + if err := fieldsCheck(fields, 3, "target "+typ); err != nil { + return 0, err + } + return trg.StatusDuration(fields[2]), nil + case "element": + if err := fieldsCheck(fields, 3, "target "+typ); err != nil { + return 0, err + } + ele := fields[2] + elekey := attributes.StringToEle(ele) + if elekey == attributes.UnknownElement { + return 0, fmt.Errorf("bad element condition: invalid element %v", ele) + } + result := combat.Durability(0) + for i := reactable.ModifierInvalid; i < reactable.EndReactableModifier; i++ { + if i.Element() == elekey && trg.Durability[i] > reactable.ZeroDur && trg.Durability[i] > result { + result = trg.Durability[i] + } + } + return float64(result), nil + } + return nil, fmt.Errorf("bad target condition: invalid type %v", typ) +} + +func evalDebuff(c *core.Core, fields []string) (any, error) { //.debuff.res.t1.name if err := fieldsCheck(fields, 4, "debuff"); err != nil { return false, err @@ -27,16 +56,15 @@ func evalDebuff(c *core.Core, fields []string) (bool, error) { } switch typ { - case "def": - return e.DefModIsActive(mod), nil - case "res": - return e.ResistModIsActive(mod), nil + case "def", "res": + log.Printf("WARN: .debuff.%v.t0.%v is deprecated, use .target0.mods.%v", typ, mod, mod) + return e.StatusIsActive(mod), nil default: return false, fmt.Errorf("bad debuff condition: invalid type %v", typ) } } -func evalElement(c *core.Core, fields []string) (float64, error) { +func evalElement(c *core.Core, fields []string) (any, error) { //.element.t1.pyro if err := fieldsCheck(fields, 3, "element"); err != nil { return 0, err @@ -49,22 +77,17 @@ func evalElement(c *core.Core, fields []string) (float64, error) { return 0, fmt.Errorf("bad element condition: %v", err) } - elekey := attributes.StringToEle(ele) - if elekey == attributes.UnknownElement { - return 0, fmt.Errorf("bad element condition: invalid element %v", ele) - } - result := combat.Durability(0) - for i := reactable.ModifierInvalid; i < reactable.EndReactableModifier; i++ { - if i.Element() == elekey && e.Durability[i] > reactable.ZeroDur && e.Durability[i] > result { - result = e.Durability[i] - } - } - return float64(result), nil + log.Printf("WARN: .element.t0.%v is deprecated, use .target0.element.%v", ele, ele) + return evalTarget(c, e, []string{"", "element", ele}) } func parseTarget(c *core.Core, trg string) (*enemy.Enemy, error) { - trg = strings.TrimPrefix(trg, "t") - tid, err := strconv.ParseInt(trg, 10, 64) + pat := regexp.MustCompile(`t(?:arget)?(\d+)`) + matches := pat.FindStringSubmatch(trg) + if len(matches) < 2 { + return nil, fmt.Errorf("invalid target %v", trg) + } + tid, err := strconv.ParseInt(matches[1], 10, 64) if err != nil { return nil, fmt.Errorf("invalid target %v", trg) } diff --git a/pkg/enemy/mods.go b/pkg/enemy/mods.go index 28209ba120..86befcfbdd 100644 --- a/pkg/enemy/mods.go +++ b/pkg/enemy/mods.go @@ -77,21 +77,33 @@ func (e *Enemy) StatusIsActive(key string) bool { return e.modIsActive(key) } func (e *Enemy) ResistModIsActive(key string) bool { return e.modIsActive(key) } func (e *Enemy) DefModIsActive(key string) bool { return e.modIsActive(key) } -//Expiry +// Duration +func (e *Enemy) getModDuration(key string) int { + m := modifier.Find(&e.mods, key) + if m == -1 { + return 0 + } + if e.mods[m].Expiry() > e.Core.F { + return e.mods[m].Expiry() - e.Core.F + } + return 0 +} +func (e *Enemy) StatusDuration(key string) int { return e.getModDuration(key) } +// Expiry func (e *Enemy) getModExpiry(key string) int { m := modifier.Find(&e.mods, key) if m != -1 { return e.mods[m].Expiry() } - //must be 0 if doesn't exist. avoid using -1 b/c that's infinite + // must be 0 if doesn't exist. avoid using -1 b/c that's infinite return 0 } func (e *Enemy) StatusExpiry(key string) int { return e.getModExpiry(key) } // Amount. -//TODO: this needs to purge if done? +// TODO: this needs to purge if done? func (e *Enemy) Resist(ai *combat.AttackInfo, evt glog.Event) float64 { var logDetails []interface{} var sb strings.Builder