clok 发表于 2024-7-2 23:57:06

[Skillw] ⭐ AttributeSystem & FightSystem⭐ - 可定制的NBT/LORE属性 ⚡ 自定义化的战斗机制⚡! [1.9-1.20.4]

本帖最后由 clok 于 2024-7-4 20:01 编辑

[![](data/attachment/album/202407/02/172037s1c297xtywtcwwxl.png)](https://github.com/Skillw/AttributeSystem)
!(https://img.shields.io/github/license/Skillw/AttributeSystem)
!(https://img.shields.io/github/v/release/Skillw/AttributeSystem)
[!(https://skillicons.dev/icons?i=java,kotlin)](https://skillicons.dev)

## 阅读前须知
- 若您习惯与AP的舒适则我不建议你使用该插件,该插件也不建议新手或菜鸟使用
- 精通该插件可以让您省去相比普通属性插件如(AP,SX)等90%的时间或资金的消耗

## 介绍
### **AttributeSystem**
**AttributeSystem** 是基于 **TabooLib VI & Pouvoir** 编写的一款多线程属性引擎, 兼容 1.9~1.20.4
不同于市面上,AttributeSystem是全面客制化的属性插件,你可以在这里定义属于你的属性,且几乎不用碰代码。
如果你想实现自主一套完整的模块化的战斗,那么FightSystem将是你的不二之选
你可以用 JavaScript 亦或是 字符串Asahi 来书写你的逻辑,这些全都由你来决定。具体可见 WIKI
本插件优化了lore属性读取性能,即便如此,我还是建议lore仅用作展示,将属性塞到NBT里
### **FightSystem**
**FightSystem**是**AttributeSystem**的附属,如果你只装载了**AttributeSystem**,哪么它就只是一副没有**灵魂的躯壳**,**AttributeSystem**不干涉你任何的战斗机制
**FightSystem**提供了一套完整的战斗系统,它的吸血,攻击,暴击,反弹等机制代码都是通过**JS**暴漏在外,用户随意更改

---
## 典型实例
### AttributeSystem
#### 属性
不同于市面上大多数**属性系统**,AS的属性系统处处都可以自定义,例如你要写一个**属性**,名称为攻击力
```
# 这是一个自定义属性
PhysicalDamage:
names:
    - "攻击力"
    - "物理伤害"
    - "物理攻击"
read-pattern: Default
#快速声明一个属性
快速属性: { }
#下面是字符串属性,感兴趣的可以看看
StringAtt:
names: [ "唯一字符串属性" ]
read-pattern: StrSkip

StringAppendAtt:
names: [ "拼接字符串属性" ]
read-pattern: StrAppend

RomenAtt:
names: [ "罗马数字" ]
read-pattern: StrRoman
```
这就是**自定义属性**
**read-pattern**为自定义捕获组,下面为范例
#### 自定义捕获组
```
Default:
type: number
matchers:
    #左边是捕获组id 右边是运算操作
    #捕获组id只能包含 a-z A-Z 0-9
    #运算操作:
    #- plus 加
    #- max 取最大
    #- min 取最小
    #- reduce 减
    #- scalar 乘
    percentMax: plus
    percentMin: plus
    valueMin: plus
    valueMax: plus
    percent: plus
    value: plus
    valueAddition: plus
    percentAddition: plus
    mul: scalar
#匹配模式(正则)
#从上到下先后匹配
#特殊字符要转义
#捕获组id只能包含 a-z A-Z 0-9
patterns:
    # 攻击力: 11-23 %
    - "{name}.*?<percentMin>-<percentMax>.*?%"
    # 攻击力: 10-20
    - "{name}.*?<valueMin>-<valueMax>"
    # 攻击力: 10 (+20) %
    - "{name}.*?<percent>.*?\\(<percentAddition>\\).*?%"
    # 攻击力: 10 %
    - "{name}.*?<percent>.*?%"
    #攻击力*10
    - "{name}.*?\\*.*?<mul>"
    # 攻击力: 100 (+50)
    - "{name}.*?<value>.*?\\(<valueAddition>\\)"
    # 攻击力: 100/攻击力 100
    - "{name}.*?<value>"
    #100 攻击力 +100
    - "<value>.*?{name}.*?<valueAddition>"
    #100 攻击力
    - "<value>.*?{name}"
#变量(PAPI / PouPAPI)
#调用变量格式: %as_att:属性ID_下面的id%
placeholders:
    #占位符id
    # 可通过 %as_att:属性id_占位符id 调用%
    #可带入捕获组 与 其他 占位符 的值
    #优先带入捕获组的
    # total min max 必须写
    total: "( <valueRandom> )*( 1 + (<percentRandom>) ) * { if check <mul> == 0 then 1 else <mul> }"
    min: "(<value> + <valueAddition> + <valueMin>)*( 1 + (<percentRandom>) ) "
    max: "(<value> + <valueAddition> + <valueMax>)*( 1 + (<percentRandom>) )"
    mul: <mul>
    value: <value> + <valueAddition>
    percent: (<percent> + <percentAddition>) /100
    valueMin: <valueMin> + <valueAddition>
    valueMax: <valueMax> + <valueAddition>
    percentMin: (<percentMin> + <percentAddition>) /100
    percentMax: (<percentMax> + <percentAddition>) /100
    valueRandom: <value> + <valueAddition> + {random <valueMin> to <valueMax>}
    percentRandom: ((<percent> + (<percentAddition>)/100) + {random <percentMin> to <percentMax>}/100)
```
这就是一个**捕获组**,它可以自定义属性的读取模式,以及自定义一个数值的释放器来达到你所预期的结果
#### 原版属性
**AttributeSystem**可不止这些,它还有一个更厉害的,就是原版属性的颠覆,任何原版属性都可以
```
#支持 PAPI/PPAPI 字符串内联函数
#会影响实体的原版属性 / 其它属性
#支持任何原版属性,例如飞行速度FlySpeed
#fly-speed:
# enable: true
# vanilla: true
# value: %as_att:FlySpeed%
max-health:
enable: true
vanilla: true
#玩家默认血量
default: 20
value: "%as_att:MaxHealth%"
movement-speed:
enable: true
vanilla: true
value: "%as_att:MovementSpeed% / 2250"
knockback-resistance:
#击退抗性
enable: true
vanilla: true
value: "%as_att:Resistance%"
#下面这些只支持玩家
attack-speed:
#单位为 攻击次数/s
enable: true
vanilla: true
value: "%as_att:AttackSpeed%"
luck:
enable: true
vanilla: true
value: "%as_att:Luck%"
```
像这样,可以把所有原版属性都放进去
#### 槽位
**AttributeSystem**支持龙核 萌芽的槽位,还支持原版槽位
```
#左ID 右槽位
player: { }
#左AS内部槽位key 右原版槽位
#2.1.0-beta 后已内置
#"头盔": "HEAD"
#"胸甲": "CHEST"
#"护腿": "LEGS"
#"靴子": "FEET"
#"主手": "HAND"
#"副手": "OFFHAND"
#读取槽位20的装备 以20th为id存入装备栏
#"20th": "20"
#这样写可以给放到槽位的装备加限制:
#"20th":
#    slot: 20
#    require: "槽位: 饰品"
entity: { }
#2.1.0-beta 后已内置
#"头盔": "HEAD"
#"胸甲": "CHEST"
#"护腿": "LEGS"
#"靴子": "FEET"
#"主手": "HAND"
#"副手": "OFFHAND"
# 萌芽槽位
germ-slots:
- "example"

```
以上是AttributeSystem所有内容
### FightSystem
如果说**AttributeSystem**只是一副躯壳,哪么**FS(FightSystem)**就是一副灵魂
#### 自定义战斗组
```
#战斗机制组ID
# attack-damage 普通攻击
# skill-api-技能id-标识符 SkillAPI技能
# damage-cause-id小写 伤害事件(会覆盖)
# https://bukkit.windit.net/javadoc/org/bukkit/event/entity/EntityDamageEvent.DamageCause.html
# 这3个是固定的
attack-damage:
namespaces: [ common fightsystem ]
#伤害类型
Physical:
    #是否启用
    enable: true
    mechanics:
      #机制id
      - mechanic: damage
      #战斗数据
      #攻击者变量: {a.PAPI变量/PouPAPI变量}
      #防御者变量: {d.PAPI变量/PouPAPI变量}
      #PouPAPI支持存活实体
      #AS提供的变量
      # origin AS处理前的伤害 一般是原版伤害 (全局)
      # force 蓄力程度弓箭蓄力程度 或 普攻蓄力程度 (仅attack-damage)
      # type 攻击类型 PVP PVE EVE (全局)
      # projectile 是否是远程攻击 true / false (全局)
      enable: |-
          check random 0 to 1 < calculate '{a.as_att:PhysicalHitChance}-{d.as_att:PhysicalDodgeChance}'
      value: |-
          set type = when of {type} {
            case == 'PVP' -> '{a.as_att:PVPDamage} - {d.as_att:PVPDefense}'
            case == 'PVE' -> '{a.as_att:PVEDamage} - {d.as_att:PVEDefense}'
            else -> 0
          }
          set projectile to if check {projectile} == true then '{a.as_att:ProjectileDamage} - {d.as_att:ProjectileDefense}' else '0'
          set damage to '{a.as_att:PhysicalDamage} + {origin}'
          set defense to if check random 0 to 1 < {a.as_att:PhysicalDefenseIgnore} then 0 else '{d.as_att:PhysicalDefense} - {a.as_att:PhysicalPenetration}'
          set force to if has force then {force} else 1
          calculate '{&damage} + {$type} + {&projectile} - {&defense} ) * {&force}'
      #机制从上到下按顺序执行
      - mechanic: crit
      enable: "check random 0 to 1 < {a.as_att:PhysicalCriticalChance}"
      #下面的机制数据可以调用上面已执行机制的执行结果 格式为{id}
      multiplier: |-
          calculate '{a.as_att:PhysicalCriticalMultiple} - {d.as_att:PhysicalCriticalDefense}'

      - mechanic: vampire
      enable: "check random 0 to 1 < calculate {a.as_att:VampireChance}"
      value: |-
          calculate '{a.as_att:VampireMultiple} - {d.as_att:VampireDefense}'

      - mechanic: flame
      enable: "check random 0 1 < {a.as_att:燃烧几率}"
      damage: |-
          max 1 {a.as_att:燃烧伤害}
      duration: |-
          max 20 calculate '{a.as_att:燃烧伤害}*3'

      - mechanic: frozen
      enable: "check random 0 1 < {a.as_att:冰冻几率}"
      value: |-
          max 1 {a.as_att:冰冻强度}
      duration: |-
          max 20 calculate '{a.as_att:冰冻强度}*3'

      - mechanic: thunder
      enable: "check random 0 1 < {a.as_att:雷击几率}"
      damage: "max 1 {a.as_att:雷击伤害}"

      - mechanic: rebound
      enable: "check random 0 1 < {d.as_att:反弹几率}"
      multiplier: "max 0.1 {d.as_att:反弹倍率}"

      - mechanic: shield
      enable: "check random 0 1 < calculate '1-{a.as_att:BlockingIgnore}'"
      reduce: "{d.as_att:Blocking}"
      #tick
      # {reduced}是实际减伤
      cooldown: "calculate '({reduced}/4)*20'"
Real:
    enable: 'check {a.as_att:RealDamage} != 0.0'
    mechanics:
      - mechanic: damage
      enable: |-
          check random 0 to 1 < calculate '{a.as_att:PhysicalHitChance}-{d.as_att:PhysicalDodgeChance}'
      value: '{a.as_att:RealDamage}'

```
如果你不会js,你就可以玩这个,它可比市面上所有属性插件都要强大
#### 自定义伤害显示
```
#伤害类型id
Physical:
#名称
name: "&6物理伤害"
#伤害显示
display:
    #攻击者
    attack:
      holo: |-
      set result to number {result}
      set crit to number {crit}
      set vampire to number {vampire}
      set common to if check &result > 0.0 then analysis "&6{format &result '#.##'}" else '&7&lMISS'
      set crit to if check &crit > 0.0 then '&4✵' else ''
      set vampire to if check &vampire > 0.0 then analysis "&a+{format &vampire '#.##'}" else ''
      join [ &crit &common &vampire ]
      chat: |-
      set result to number {result}
      set crit to number {crit}
      set vampire to number {vampire}
      set prefix to '&d{name}&5: '
      set common to if check &result > 0.0 then "&6{format &result '#.##'}" else '&7&lMISS'
      set crit to if check &crit > 0.0 then '&4✵' else ''
      set vampire to if check &vampire > 0.0 then "&a+{ format &vampire '#.##' }" else ''
      join [ &prefix &crit &common &vampire ]
      title: "null"
      sub-title: |-
      set result to number {result}
      set vampire to number {vampire}

      set crit to number {crit}
      set crit to if check &crit > 0.0 then '&4暴击' else '&4'
      set common to if check &result > 0.0 then "{&crit}                &c{ format &result '#.##' }" else '&7&lMISS'
      set vampire to if check &vampire > 0.0 then "&a+{ format &vampire '#.##' }" else ''

      join [ '!' &common &vampire ]
      action-bar: |-
      set result to number {result}
      set crit to number {crit}
      set vampire to number {vampire}

      set prefix to '&d{name}&5: '
      set common to if check &result > 0.0 then "&6{ format &result '#.##' }" else '&7&lMISS'
      set crit to if check &crit > 0.0 then '&4✵' else ''
      set vampire to if check &vampire > 0.0 then "&c吸血&a{ format &vampire '#.##' }" else ''
      join [ &prefix &crit &common &vampire ]
    defend:
      holo: |-
      set result to number {result}
      set crit to number {crit}

      set subtract to '&c- '
      set common to if check &result != 0 then "&6{format &result '#.##'}" else '&7&lMISS'
      set crit to if check &crit > 0.0 then '&4✵' else ''
      join [ &subtract &crit &common ]
      chat: |-
      set result to number {result}
      set crit to number {crit}

      set prefix to '&d{name}&5: '
      set common to if check &result != 0 then "&6{format &result '#.##'}" else '&7&lMISS'
      set crit to if check &crit > 0.0 then '&4✵' else ''
      join [ &prefix &crit &common ]
      title: |-
      set crit to number {crit}

      if check &crit != 0 then '&4受到暴击' else '&4'
      sub-title: |-
      set result to number {result}
      set vampire to number {vampire}
      set crit to if check &crit > 0.0 then '&c-&4✵ ' else '&c- '
      set common to if check &result > 0.0 then "&6{ format &result '#.##' }" else '&7&lMISS'
      join [ '!' &crit &common ]
      action-bar: |-
      set result to number {result}
      set crit to number {crit}
      set vampire to number {vampire}

      set prefix to '&d{name}&5: '
      set common to if check &result > 0.0 then "&6{ format &result '#.##' }" else '&7&lMISS'
      set crit to if check &crit > 0.0 then '&4✵' else ''
      join [ &prefix &crit &common ]

```
用于显示伤害,当你看到holo,chat,title,sub-title,actionbar这类用于信息输出的,你应该就明白了,**FS**自带一套完美的伤害显示系统!还可以让玩家**个性化选择**需要的**伤害显示**!
#### 自定义机制(JS党狂喜)
```
//@Mechanic(flame)
function flame(data, context, damageType) {
    const enable = data.handle(context.get("enable"));
    if (enable.toString() != "true") {
      return 0.0;
    }
    const attacker = data.attacker;
    const defender = data.defender;
    const damage = toDouble(data.handle(context.get("damage")));
    const duration = toDouble(data.handle(context.get("duration")));
    task(function (task) {
      defender.setFireTicks(duration);
    });
    data.damageSources.put("fire", Plus.element(damage));
    if (attacker instanceof Player)
      attacker.sendMessage(
            color(
                "&c&l燃烧!&f你点燃了对方,持续 &b" +
                duration / 20 +
                "s &f, 造成了&6&l" +
                damage +
                "&f点伤害!"
            )
      );
    if (defender instanceof Player)
      defender.sendMessage(
            color(
                "&c&l燃烧!&f对方点燃了你,持续 &b" +
                duration / 20 +
                "s &f, 造成了&6&l" +
                damage +
                "&f点伤害!"
            )
      );
    return damage;
}

// 冰冻

//@Mechanic(frozen)
function frozen(data, context, damageType) {
    const enable = data.handle(context.get("enable"));
    if (enable.toString() != "true") {
      return 0.0;
    }
    const attacker = data.attacker;
    const defender = data.defender;
    const value = toDouble(data.handle(context.get("value")));
    const duration = toDouble(data.handle(context.get("duration")));

    AttrAPI.addAttribute(
      defender,
      "frozen",
      listOf("移动速度: -" + value),
      false
    );
    taskLater(
      duration.longValue(),
      function (task) {
            AttrAPI.removeAttribute(defender, "frozen");
      }
    );
    if (attacker instanceof Player)
      attacker.sendMessage(
            color(
                "&b&l冰冻!&f你冰冻了对方,持续 &9" +
                duration / 20 +
                "s &f, 减少了&6&l" +
                value +
                "&f点移动速度!"
            )
      );
    if (defender instanceof Player)
      defender.sendMessage(
            color(
                "&b&l冰冻!&f对方冰冻了你,持续 &9" +
                duration / 20 +
                "s &f, 减少了&6&l" +
                value +
                "&f点移动速度!"
            )
      );
    return value;
}

// 雷击

//@Mechanic(thunder)
function thunder(data, context, damageType) {
    const enable = data.handle(context.get("enable"));
    if (enable.toString() != "true") {
      return 0.0;
    }
    const attacker = data.attacker;
    const defender = data.defender;
    const damage = toDouble(data.handle(context.get("damage")));
    task(function (task) {
      defender.world.strikeLightningEffect(defender.location);
    });
    data.damageSources.put("thunder", Plus.element(damage));
    if (attacker instanceof Player)
      attacker.sendMessage(
            color("&e&l雷击!&f你雷击了对方,造成了&6&l" + damage + "&f点伤害!")
      );
    if (defender instanceof Player)
      defender.sendMessage(
            color("&e&l雷击!&f对方雷击了你,造成了&6&l" + damage + "&f点伤害!")
      );
    return damage;
}

// 反弹
//@Mechanic(rebound)
function rebound(data, context, damageType) {
    const enable = data.handle(context.get("enable"));
    if (enable.toString() != "true") {
      return 0.0;
    }
    const attacker = data.attacker;
    const defender = data.defender;
    const multiplier = toDouble(data.handle(context.get("multiplier")));
    const damage = data.calResult() * multiplier;
    if (damage <= 0) return 0;
    task(function (task) {
      AttributeSystemAPI.skipNextDamageCal();
      attacker.damage(damage, defender);
    });
    if (attacker instanceof Player)
      attacker.sendMessage(
            color("&e&l反弹!&f对方反弹了你,造成了&6&l" + damage + "&f点伤害!")
      );
    if (defender instanceof Player)
      defender.sendMessage(
            color("&e&l反弹!&f你反弹了对方,造成了&6&l" + damage + "&f点伤害!")
      );
    return damage;
}

PotionEffectType = find("org.bukkit.potion.PotionEffectType");
PotionEffect = find("org.bukkit.potion.PotionEffect");
// 药水
//@Mechanic(potion)
function potion(data, context, damageType) {
    const enable = data.handle(context.get("enable"));
    if (enable.toString() != "true") {
      return 0.0;
    }
    const attacker = data.attacker;
    const defender = data.defender;
    const type = toDouble(data.handle(context.get("type")));
    if (Data.containsKey(attacker.uniqueId + type)) return -1;
    const potionType = PotionEffectType.getByName(type);
    if (potionType == null) return -2;
    const level = toDouble(data.handle(context.get("level")));
    const duration = toDouble(data.handle(context.get("duration")));
    const cooldown = toDouble(data.handle(context.get("cooldown")));
    task(function (task) {
      Data.put(attacker.uniqueId + type, true);
      defender.addPotionEffect(new PotionEffect(potionType, duration, level));
    });
    taskLater(
      cooldown,
      function (task) {
            Data.remove(attacker.uniqueId + type);
      }
    );
    return duration;
}
function toDouble(obj) {
    if (obj === null || typeof obj === "undefined") {
      return 0.0;
    }
    if (typeof obj === "number" || obj instanceof Number) {
      return Number(obj);
    }
    var parsed = parseFloat(obj);
    if (!isNaN(parsed)) {
      return parsed;
    }
    return 0.0;
}
```
依赖**(https://github.com/Skillw/Pouvoir)**强大的JS系统与脚本注解系统,可以快速自定义一个机制!
#### 玩家个性化系统
允许玩家选择自定义的**messagetype**,目前自带的有**holo,chat,title,sub-title,actionbar**,当然了,也可以使用**脚本注解**进行拓展

#### 支持的插件
- **MythicMobs**
- **龙核**
- **萌芽**
- **Magic**
- **crackshot枪械**
- **PVPManager**
- **SkillAPI**
MythicMobs支持技能,甚至怪物普攻的替换
龙核萌芽支持伤害显示与槽位
Magic支持技能的战斗机制组
枪械支持自定义战斗机制组
SkillAPI支持自定义战斗机制组

## 未来计划
1. 迁移至Minestom核心,目前SkillW已经在写RPGMaker与DevoutServer,RPGMaker是转为RPG服务器所写的多线程服务端核心,预计2025年下半年完成
2. 提提建议,多提(https://github.com/Skillw/AttributeSystem/issues)

## 下载
- (https://github.com/Skillw/Pouvoir/releases)
- (https://github.com/Skillw/AttributeSystem/releases)
- (https://github.com/Skillw/FightSystem/releases)

## 相关链接
相关链接

Github https://github.com/Glom-c/AttributeSystem
QQ群 https://qm.qq.com/q/2kaZq7PEqo
WIKI https://www.skillw.com/zh-CN/docs/attsystem/intro
JavaDoc http://doc.skillw.com/attsystem/
爱发电 https://afdian.net/@glom_


## 附属列表
- (https://github.com/Skillw/BuffSystem)

## 最后
#### 感谢所有**AttributeSystem & FightSystem**项目的贡献者
#### 如果你喜欢该项目,不妨评个分 把项目star一下

xiaojian205 发表于 2024-7-11 21:37:02

这个插件正适合我感谢,楼主的发布

Ais_de1 发表于 2024-7-27 23:37:11

非常强大的插件
页: [1]
查看完整版本: [Skillw] ⭐ AttributeSystem & FightSystem⭐ - 可定制的NBT/LORE属性 ⚡ 自定义化的战斗机制⚡! [1.9-1.20.4]