阅读前须知
- 若您习惯与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;
}
依赖Pouvoir强大的JS系统与脚本注解系统,可以快速自定义一个机制!
玩家个性化系统
允许玩家选择自定义的messagetype,目前自带的有holo,chat,title,sub-title,actionbar,当然了,也可以使用脚本注解进行拓展
支持的插件
- MythicMobs
- 龙核
- 萌芽
- Magic
- crackshot枪械
- PVPManager
- SkillAPI
MythicMobs支持技能,甚至怪物普攻的替换
龙核萌芽支持伤害显示与槽位
Magic支持技能的战斗机制组
枪械支持自定义战斗机制组
SkillAPI支持自定义战斗机制组
未来计划
- 迁移至Minestom核心,目前SkillW已经在写RPGMaker与DevoutServer,RPGMaker是转为RPG服务器所写的多线程服务端核心,预计2025年下半年完成
- 提提建议,多提issues
下载
相关链接
相关链接
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_
附属列表
最后
感谢所有AttributeSystem & FightSystem项目的贡献者
如果你喜欢该项目,不妨评个分 把项目star一下