英雄核心 API

英雄核心 API

Hytale的RPG属性、战斗和进度框架。

实用

HeroCore > https://github.com/HerotaleRPG/herocore/

HEROES RPG MOD 的必要依赖

Hytale 的 RPG 属性、战斗与成长框架。

HeroCore 是一个扩展 Hytale 原生 ECS(实体组件系统)的模式与公式库。它提供了主要属性、派生属性计算、多阶段伤害/治疗管线、状态效果、等级成长等功能——全部通过 Hytale 原生的 EntityStatMapStaticModifier 和 ECS 事件系统来驱动。无需桥接类、无需并行的属性引擎、无需自定义事件总线。

版本: 0.2.0 服务端: Hytale 2026.02.17+ Java: 25 作者: Kainzo


目录

  1. 架构概览
  2. 插件生命周期
  3. ECS 组件
  4. 属性系统
  5. 属性派生
  6. 伤害管线
  7. 治疗管线
  8. 战斗状态与超时
  9. 状态效果
  10. 升级与成长
  11. 配置
  12. 下游集成指南
  13. API 参考
  14. 构建

架构概览

HeroCorePlugin (JavaPlugin)
│
├── 模式层 ── HeroCore 拥有的定义
│   ├── RPGAttribute 枚举         62 个属性(主要 + 次要)
│   ├── DamageType 枚举           PHYSICAL, MAGICAL, FIRE, ICE 等
│   ├── HealType 枚举             SPELL, POTION, REGEN_TICK, PASSIVE, ENVIRONMENTAL
│   ├── DamageFlag 枚举           CRIT, DOT, AOE, MELEE, RANGED 等
│   └── CoreConfig                所有调节参数(伤害、治疗、派生、再生)
│
├── 数学层 ── 纯函数,可单元测试,无副作用
│   ├── AttributeDerivationFormulas   主要属性 → 次要属性加成数值
│   ├── DamageFormulas                抗性曲线,暴击判定,吸血
│   └── HealFormulas                  治疗强度缩放,治疗暴击
│
├── ECS 组件 ── 在 setup() 中注册,存储在实体上
│   ├── HeroCoreStatsComponent        主要基础值 (STR/DEX/INT/FAITH/VIT/RES)    持久化
│   ├── HeroCoreProgressionComponent  等级,经验,升级所需经验                    持久化
│   ├── CombatStateComponent          战斗状态标志 + dt 累积器                   临时
│   └── StatusEffectIndexComponent    激活效果 ID + 剩余秒数                     临时
│
├── ECS 系统 ── 全部继承原生 Hytale 系统类型
│   ├── HeroCoreSetupSystem           HolderSystem — 确保实体添加时拥有组件
│   ├── AttributeDerivationSystem     EntityTickingSystem — 写入 StaticModifiers (在 Recalculate 之前)
│   ├── CombatTimeoutSystem           DelayedEntitySystem — 超时后退出战斗
│   └── StatusEffectTickSystem        DelayedEntitySystem — 使计时效果过期
│
├── 伤害管线 ── EntityEventSystem 链 (SystemDependency 排序)
│   AttackDamageBonusSystem → FallDamageReductionSystem → ResistanceMitigationSystem
│   → CriticalHitSystem → LifestealSystem → MinimumDamageSystem → DamageApplicationSystem
│
├── 治疗管线 ── EntityEventSystem 链
│   HealingPowerScalingSystem → HealingReceivedBonusSystem → HealCritSystem
│
├── 注册表 ── 供下游插件使用的 API 门面
│   ├── LevelingRegistry          经验授予,等级查询,档案管理
│   ├── MobRegistry               生物/NPC 档案注册与查找
│   └── ZoneModifierRegistry      区域修饰器 (v0.3 计划)
│
└── 原生 Hytale (使用但不替换)
    ├── EntityStatMap          所有属性值的单一数据源
    ├── StaticModifier         所有修饰器的应用
    ├── RegeneratingValue      再生 (通过 EntityStatType JSON 资源配置)
    ├── BuilderCodec           组件持久化
    └── CommandBuffer.invoke()  ECS 事件调度

设计原则

  1. 无并行基础设施。 HeroCore 将 StaticModifier 条目写入 EntityStatMap。没有第二个属性容器,也没有在系统间同步数值的桥接。
  2. 运行时处处使用 Ref<EntityStore> UUID 仅用于持久化。所有方法、事件和系统传递的都是实时实体句柄。
  3. 所有系统均继承原生 Hytale 类型。 EntityTickingSystemDelayedEntitySystemEntityEventSystemHolderSystem — 无自定义系统抽象。
  4. 纯数学运算与 ECS 接线分离。 DamageFormulasHealFormulasAttributeDerivationFormulas 是静态纯函数。单元测试无需服务端实例。
  5. 所有持久化使用 BuilderCodec 加入/退出事件时不使用 Gson 序列化。组件作为实体存储保存周期的一部分进行序列化。

插件生命周期

HeroCorePlugin 继承 JavaPlugin 并遵循标准 Hytale 模块模式:

阶段 执行内容
preLoad() mods/herocore/config.json 加载 CoreConfig(若无则回退到内置默认值)
setup() 通过 getEntityStoreRegistry() 注册全部 4 个组件、4 个 ECS 事件类型和所有系统
start() 通过 HeroCoreStatTypes.update() 解析自定义属性类型索引并进行验证
shutdown() 记录清理关闭日志

清单文件

{
    "Group": "Herotale",
    "Name": "Herocore",
    "Version": "0.2.0",
    "Main": "net.herotale.herocore.impl.HeroCorePlugin",
    "IncludesAssetPack": true
}

ECS 组件

所有组件通过 HeroCoreComponentRegistry.registerComponents()setup() 中注册。每个组件遵循标准模式:实现 Component<EntityStore>、拷贝构造函数、clone()、静态 ComponentType 句柄。

HeroCoreStatsComponent (持久化)

持有六个主要 RPG 属性的基础值。这些是 AttributeDerivationSystem 的输入。

字段 类型 描述
strength float 物理伤害、挖掘速度、方块强度
dexterity float 暴击率、攻击速度、闪避、移动速度
intelligence float 法术强度、法力值、法术暴击、魔法抗性
faith float 治疗强度、法力值、法力回复、治疗暴击、增益强度
vitality float 最大生命值、生命回复、护甲、物理抗性
resolve float 控制抗性、减益抗性、体力回复、威胁值

编解码键: HC_StrengthHC_DexterityHC_IntelligenceHC_FaithHC_VitalityHC_Resolve

HeroCoreProgressionComponent (持久化)

追踪玩家等级和经验值成长。

字段 类型 默认值 描述
level int 1 当前等级
currentXP float 0 已累积的升级经验值
xpToNextLevel float 100 下一次升级所需经验值

编解码键: HC_LevelHC_CurrentXPHC_XPToNextLevel

CombatStateComponent (临时)

使用基于 dt 的计时(非挂钟时间)追踪实体是否处于战斗状态。通过 enterCombat() 重置,通过 tickElapsed(dt) 累积。

方法 描述
enterCombat() 设置战斗状态,将计时器重置为 0
tickElapsed(float dt) 累积自上次受伤以来的时间(仅在战斗状态时)
isInCombat() 战斗状态时返回 true
getSecondsSinceLastDamage() enterCombat() 以来经过的秒数

StatusEffectIndexComponent (临时)

StatusEffectTickSystem 读取的轻量索引,用于使效果过期。实际的属性修改作为 StaticModifier 条目存储在 EntityStatMap 中。效果过期时,StatusEffectTickSystem 使用跟踪的 ModifierRef 列表自动移除所有关联的修饰器。

public static class EffectEntry {
    public final int stacks;
    public float remainingSeconds; // 每个滴答由 dt 递减

    // 修饰器跟踪,用于过期时自动清理
    public void trackModifier(int statIndex, String modifierKey);
    public List<ModifierRef> getModifierRefs();

    public record ModifierRef(int statIndex, String modifierKey) {}
}
方法 描述
addEffect(id, stacks, durationSeconds) 注册一个新的效果
getEffect(id) 通过 ID 获取 EffectEntry(用于在应用修饰器后调用 trackModifier()
removeEffect(id) 通过 ID 移除效果
getActiveEffects() 所有激活效果的映射表

属性系统

RPGAttribute 枚举

RPGAttribute 是所有 RPG 属性的权威分类。它定义了 62 个属性,涵盖多个类别:

类别 示例
主要属性 STRENGTHDEXTERITYINTELLIGENCEFAITHVITALITYRESOLVE
战斗 — 物理 MAX_HEALTHARMORATTACK_DAMAGEATTACK_SPEEDCRIT_CHANCELIFESTEAL
战斗 — 魔法 SPELL_POWERSPELL_CRIT_CHANCEMAGIC_PENETRATIONMAGIC_RESIST
元素 ELEMENTAL_DAMAGE_FIREELEMENTAL_RESIST_ICEICE_RESISTANCE_PERCENT
治疗 HEALING_POWERHEALING_RECEIVED_BONUSHEAL_CRIT_CHANCE
资源 MAX_MANAMAX_STAMINAMANA_REGENHEALTH_REGEN
机动 MOVE_SPEEDJUMP_HEIGHTMINING_SPEED
成长 XP_GAIN_MULTIPLIERRARE_DROP_CHANCE

HeroCoreStatTypes

在运行时从 EntityStatType JSON 资源解析自定义 RPG 属性类型索引。遵循 DefaultEntityStatTypes 的模式。

// 在 start() 之后,这些方法返回有效的 int 索引
HeroCoreStatTypes.getCritChance();           // herocore:crit_chance
HeroCoreStatTypes.getAttackDamage();         // herocore:attack_damage
HeroCoreStatTypes.getSpellPower();           // herocore:spell_power
HeroCoreStatTypes.getPhysicalResistance();   // herocore:physical_resistance
HeroCoreStatTypes.getMagicResist();          // herocore:magic_resist
HeroCoreStatTypes.getAttackSpeed();          // herocore:attack_speed
HeroCoreStatTypes.getMoveSpeed();            // herocore:move_speed
HeroCoreStatTypes.getMiningSpeed();          // herocore:mining_speed
HeroCoreStatTypes.getHealingPower();         // herocore:healing_power
HeroCoreStatTypes.getCritDamageMultiplier(); // herocore:crit_damage_multiplier

// 通过资源 ID 字符串动态查找
HeroCoreStatTypes.getIndex("herocore:lifesteal");

// 辅助方法:从实体引用读取属性值
float value = HeroCoreStatTypes.getStatValue(entityRef, statIndex);

HeroCoreModifiers

HeroCore 使用的所有修饰器键都是 HeroCoreModifiers 中的常量。修饰器调用中没有内联字符串。

// 派生属性(由 AttributeDerivationSystem 写入)
HeroCoreModifiers.DERIVED_MAX_HEALTH       // "HC_derived_max_health"
HeroCoreModifiers.DERIVED_CRIT_CHANCE      // "HC_derived_crit_chance"
HeroCoreModifiers.DERIVED_SPELL_POWER      // "HC_derived_spell_power"
HeroCoreModifiers.DERIVED_ATTACK_DAMAGE    // "HC_derived_attack_damage"
HeroCoreModifiers.DERIVED_MOVE_SPEED       // "HC_derived_move_speed"
HeroCoreModifiers.DERIVED_ARMOR            // "HC_derived_armor"
// ... 共 30+ 个常量

// 动态键构建器
HeroCoreModifiers.effect("burn", "health");   // "HC_effect_burn_health"
HeroCoreModifiers.gear("chest_iron", "armor"); // "HC_gear_chest_iron_armor"
HeroCoreModifiers.derived("custom_stat");      // "HC_derived_custom_stat"

属性派生

工作原理

AttributeDerivationSystem 是一个 EntityTickingSystem,每帧运行。它从 HeroCoreStatsComponent 读取六个主要属性,并将派生属性加成作为 StaticModifier 条目写入实体的 EntityStatMap

它实现了 EntityStatsSystems.StatModifyingSystem(标记接口),并通过 SystemDependency 声明 BEFORE EntityStatsSystems.Recalculate。这确保了:

  1. HeroCore 将派生修饰器写入 EntityStatMap
  2. Hytale 原生的 Recalculate 系统处理所有修饰器
  3. 最终计算值可供游戏系统读取

下游的 stat-modifying 系统也必须声明 BEFORE EntityStatsSystems.Recalculate 以避免竞争条件:

@Nonnull
@Override
public Set<Dependency<EntityStore>> getDependencies() {
    return Set.of(
        new SystemDependency<>(Order.BEFORE, EntityStatsSystems.Recalculate.class)
    );
}
HeroCoreStatsComponent (主要基础值)
        │
        ▼
AttributeDerivationFormulas.computeDerived()  ← 纯数学运算
        │
        ▼
EntityStatMap.putModifier()  ← 写入命名修饰器
        │
        ▼
EntityStatsSystems.Recalculate  ← 原生 Hytale (在之后运行)

所有修饰器使用 ADDITIVE 叠加到 ModifierTarget.MAX 上。 这是有意为之——基于百分比的属性应叠加相加。资源的 max 字段负责上限处理。

派生公式

派生属性 公式 来源属性
物理抗性 (VIT + RES) / 2 活力,意志
法术强度 (INT + FAITH) / 2 智力,信仰
暴击率 min(1.0, DEX / 200) 敏捷
暴击伤害倍率 1.5 + (STR + INT) / 500 力量,智力
攻击伤害 STR / 10 力量
移动速度 1.0 + DEX / 500 敏捷
攻击速度 1.0 + DEX / 300 敏捷
魔法抗性 INT / 10 智力
治疗强度 FAITH / 10 信仰
挖掘速度 1.0 + STR / 500 力量

这些公式位于 AttributeDerivationFormulas.computeDerived() 中——一个纯静态函数。所有公式都有 13 个单元测试覆盖。


伤害管线

伤害流经由 SystemDependency 排序的 EntityEventSystem<EntityStore, HeroCoreDamageEvent> 实现链。每个阶段从 EntityStatMap 读取属性值并修改事件的 modifiedAmount

管线阶段(按执行顺序)

# 系统 功能
1 AttackDamageBonusSystem 按攻击者的 attack_damage 属性缩放伤害
2 FallDamageReductionSystem 按受害者的 fall_damage_reduction 属性减少坠落伤害(仅对 DamageType.FALL 生效——跳过所有其他伤害类型)。HeroCore 新增——不在基础重构指南管线中。通过 SystemDependency 声明 AFTER AttackDamageBonusSystem
3 ResistanceMitigationSystem 应用元素/魔法抗性(非物理抗性——那是原生系统处理的)
4 CriticalHitSystem crit_chance 判定暴击,乘以 crit_damage_multiplier
5 LifestealSystem 按伤害的 lifesteal % 治疗攻击者(派发 HeroCoreHealEvent
6 MinimumDamageSystem 强制执行可配置的最小伤害下限
7 DamageApplicationSystem 通过 subtractStatValue() 将最终伤害写入 EntityStatMap

HeroCoreDamageEvent

public class HeroCoreDamageEvent extends CancellableEcsEvent {
    @Nullable Ref<EntityStore> attacker;  // null = 环境伤害
    DamageType damageType;                // PHYSICAL, FIRE 等
    float rawAmount;                      // 原始伤害
    float modifiedAmount;                 // 当前值(由管线修改)
    boolean isCrit;                       // 由 CriticalHitSystem 设置
}

派发伤害

从任何可以访问 CommandBuffer 的 ECS 系统:

// 向目标实体派发伤害——流经整个管线
cb.invoke(targetRef, new HeroCoreDamageEvent(attackerRef, DamageType.PHYSICAL, 25f));

// 环境伤害(无攻击者)
cb.invoke(targetRef, new HeroCoreDamageEvent(null, DamageType.FIRE, 10f));

CommandBuffer.invoke()同步的——事件会按顺序流过所有管线阶段。

取消伤害

任何管线阶段(或下游插件的系统)都可以取消:

event.setCancelled(true); // 所有后续阶段都会检查 isCancelled()

治疗管线

与伤害模式相同。三个 EntityEventSystem<EntityStore, HeroCoreHealEvent> 阶段:

# 系统 功能
1 HealingPowerScalingSystem 按治疗者的 healing_power 属性缩放治疗量(适用于 SPELL/PASSIVE 治疗,可选 REGEN_TICK)
2 HealingReceivedBonusSystem 应用目标的 healing_received_bonus 属性
3 HealCritSystem heal_crit_chance 判定治疗暴击,乘以 heal_crit_multiplier

HeroCoreHealEvent

public class HeroCoreHealEvent extends CancellableEcsEvent {
    @Nullable Ref<EntityStore> healer;
    HealType healType;       // SPELL, POTION, REGEN_TICK, PASSIVE, ENVIRONMENTAL
    float rawAmount;
    float modifiedAmount;
    boolean isCrit;
}

派发治疗

cb.invoke(targetRef, new HeroCoreHealEvent(healerRef, HealType.SPELL, 50f));

战斗状态与超时

CombatStateComponent

实体在受到伤害时进入战斗状态(DamageApplicationSystem 调用 combat.enterCombat())。计时器通过世界滴答 dt 累积——不依赖挂钟时间。

CombatTimeoutSystem

一个 DelayedEntitySystem,每 0.5 秒触发一次(有意比 StatusEffectTickSystem 的 0.25 秒慢——战斗超时不需亚秒级精度,0.5 秒可减少不必要的迭代)。遍历拥有 CombatStateComponent 的实体,检查 secondsSinceLastDamage > combatTimeoutSeconds(可配置,默认 8.0 秒,来自配置)。超时到时:

  1. 设置 inCombat = false
  2. 通过 cb.invoke() 派发 CombatExitEvent——下游插件可以响应此事件

CombatExitEvent

public class CombatExitEvent extends EcsEvent {
    // 标记事件——无字段。战斗超时到期时派发。
}

状态效果

效果工作方式

  1. 应用:StaticModifier 条目写入 EntityStatMap,在 StatusEffectIndexComponent 中记录效果,并调用 trackModifier() 注册每个修饰器以便自动清理
  2. 滴答: StatusEffectTickSystem 每 0.25 秒将 remainingSeconds 减去 dt(有意设快——状态效果需要亚秒级精度)
  3. 过期:remainingSeconds <= 0 时,StatusEffectTickSystemEntityStatMap 中移除所有追踪的修饰器(通过 ModifierRef 列表),并移除索引条目。滴答系统拥有完整的清理权——调用者只需在应用时调用 trackModifier()

应用效果(下游代码)

// 写入属性修饰器
EntityStatMap statMap = store.getComponent(ref, EntityStatsModule.get().getEntityStatMapComponentType());
String modKey = HeroCoreModifiers.effect("burn", "health_regen");
statMap.putModifier(healthRegenIndex, modKey,
    new StaticModifier(ModifierTarget.MAX, CalculationType.ADDITIVE, -5f));

// 在索引中记录以便自动过期
StatusEffectIndexComponent index = store.getComponent(ref, StatusEffectIndexComponent.getComponentType());
index.addEffect("burn", 1, 10f); // 1 层,10 秒

// 注册修饰器以便过期时自动清理
StatusEffectIndexComponent.EffectEntry entry = index.getEffect("burn");
entry.trackModifier(healthRegenIndex, modKey);
// StatusEffectTickSystem 将在效果过期时移除该修饰器

StatusEffectDefinition (API 数据记录)

// 在 api/status/StatusEffect.java 中——构建器模式
StatusEffect effect = StatusEffect.builder()
    .id("herocore:burn")
    .durationSeconds(5f) // 秒——与基于 dt 的 ECS 计时一致
    .source("fire_trap")
    .stacks(3)
    .build();

升级与成长

经验值曲线

HeroCore 通过 XpCurve 提供三种经验值曲线实现:

类型 工厂方法 描述
指数型 XpCurve.exponential(baseXp, growthRate) 标准 RPG 曲线
线性型 XpCurve.linear(xpPerLevel) 每级固定经验值
离散型 XpCurve.discrete(long[] thresholds) 自定义阈值

通过 XpCurveFactory 提供的预设:

  • XpCurveFactory.standard() — 基数 100,增长率 1.15
  • XpCurveFactory.fast() — 基数 50,增长率 1.10
  • XpCurveFactory.slow() — 基数 200,增长率 1.25
  • XpCurveFactory.flatCurve() — 每级 1000 经验值

LevelingProfile

LevelingProfile profile = LevelingProfile.builder()
    .id("combat")
    .maxLevel(60)
    .xpCurve(XpCurve.exponential(100, 1.15))
    .build();

LevelingRegistry

注册档案和授予经验值:

LevelingRegistry reg = HeroCore.get().getLevelingRegistry();
reg.register(profile);

// 在具有 Ref、Store 和 CommandBuffer 访问权限的 ECS 系统中:
reg.grantXP(entityRef, store, cb, "combat", 150.0, XPSource.KILL);
int level = reg.getLevel(entityRef, store, "combat");

等级事件

// EcsEvent——通过 ECS 事件系统派发
public class LevelUpEvent extends EcsEvent {
    int oldLevel, newLevel;
}
public class LevelDownEvent extends EcsEvent {
    int oldLevel, newLevel;
}

当经验值获得导致实体计算等级提升时(通过 LevelingRegistry.grantXP())派发 LevelUpEvent。当通过 LevelingRegistry.setXP() 明确将经验值设低,且重新计算的等级低于先前等级时,派发 LevelDownEvent。两个事件都携带旧等级和新等级值。


配置

配置从 mods/herocore/config.json 加载。如果该文件不存在,则从内置的 hero-core-defaults.json 复制默认值。

配置段

关键字段
modifierStacking maxPercentAdditivemultiplicativeCap
damage critDamageBaseMultiplier(默认 2.0)、minimumDamage(默认 1.0)、maxResistanceReduction(默认 0.9)
heal healCritBaseMultiplierhealingPowerScalesRegenTickhealingPowerScalesPassive
resourceRegen tickIntervalSeconds(默认 2.0)、outOfCombatBonusMultipliercombatTimeoutSeconds(默认 8.0)
leveling defaultMaxLevel(60)、sourceWeights(每个 XPSource 的倍率)
attributeDerivation 每个属性的派生系数(活力、力量、敏捷、智力、信仰、意志)
defenseDerivation 每个伤害类型的抗性派生系数

Heroes RPG Mod 实现指南

本节是一个实用清单,用于将 HeroCore 接入完整的 RPG Mod:升级与经验值、主要属性、派生/次要属性以及装备集成。

1)升级与经验值

  1. 定义一个或多个 LevelingProfile 条目(战斗、制作、采集等)。
  2. 在插件 setup()start() 期间在 LevelingRegistry 中注册档案。
  3. 通过 LevelingRegistry.grantXP() 从游戏事件中授予经验值。
LevelingRegistry leveling = HeroCore.get().getLevelingRegistry();
leveling.register(LevelingProfile.builder()
    .id("combat")
    .maxLevel(60)
    .xpCurve(XpCurve.exponential(100, 1.15))
    .build());

// 在具有 Store + CommandBuffer 的 ECS 系统中颁发经验值
leveling.grantXP(entityRef, store, cb, "combat", 120.0, XPSource.KILL);

通过 ECS 事件响应成长:

reg.registerSystem(new EntityEventSystem<EntityStore, LevelUpEvent>(LevelUpEvent.class) {
    @Override
    public Query<EntityStore> getQuery() {
        return HeroCoreStatsComponent.getComponentType();
    }

    @Override
    protected void handle(int index, ArchetypeChunk<EntityStore> chunk,
                          Store<EntityStore> store, CommandBuffer<EntityStore> cb,
                          LevelUpEvent event) {
        HeroCoreStatsComponent stats = chunk.getComponent(index, HeroCoreStatsComponent.getComponentType());
        stats.setStrength(stats.getStrength() + 1f);
        stats.setVitality(stats.getVitality() + 2f);
    }
});

2)主要属性与次要属性

  • 主要属性 位于 HeroCoreStatsComponent 中(STR/DEX/INT/FAITH/VIT/RES)。
  • 次要属性AttributeDerivationSystem 每帧派生并写入 EntityStatMap
  • 派生值由 AttributeDerivationFormulas 计算,并作为 StaticModifier 条目应用。

如果你修改主要属性,不要手动重新计算次要属性。下一个滴答将更新修饰器:

HeroCoreStatsComponent stats = store.getComponent(ref, HeroCoreStatsComponent.getComponentType());
stats.setDexterity(stats.getDexterity() + 5f);

读取次要属性始终通过 EntityStatMap

EntityStatMap statMap = store.getComponent(ref, EntityStatsModule.get().getEntityStatMapComponentType());
int critIdx = HeroCoreStatTypes.getCritChance();
float critChance = statMap.get(critIdx).get();

3)装备、物品栏与物品事件

HeroCore 不直接实现物品栏或装备事件。使用 Hytale 的物品栏或装备事件(或你自己的 Mod 的物品系统),并将其转换为属性修饰器。

推荐模式:

  1. 监听物品装备/卸下或物品栏变更事件(在 HeroCore 外部)。
  2. 使用 HeroCoreModifiers.gear() 键应用或移除修饰器。
  3. 如果需要,将任何装备状态存储在你自己的组件中,而不是 HeroCore 中。
// 示例:应用胸甲护甲加成
String key = HeroCoreModifiers.gear("chestplate_iron", "armor");
int armorIdx = HeroCoreStatTypes.getPhysicalResistance();
statMap.putModifier(armorIdx, key,
    new StaticModifier(Modifier.ModifierTarget.MAX, StaticModifier.CalculationType.ADDITIVE, 12f));

// 卸下时移除
statMap.removeModifier(armorIdx, key);

如果你的项目已经有物品栏事件层,这就是将物品转换为属性效果的桥接点。


下游集成指南

依赖声明

在你的下游插件的 manifest.json 中:

{
    "Dependencies": {
        "Herotale:Herocore": "*"
    }
}

读取属性

所有属性都在 EntityStatMap 中。HeroCore 提供属性索引解析器:

// 从实体读取 HeroCore 定义的属性
EntityStatMap stats = store.getComponent(ref, EntityStatsModule.get().getEntityStatMapComponentType());
int critIdx = HeroCoreStatTypes.getCritChance();
float critChance = stats.get(critIdx).get(); // 0.0–1.0

// 读取原生 Hytale 属性
int healthIdx = DefaultEntityStatTypes.getHealth();
float health = stats.get(healthIdx).get();

// 便捷辅助方法
float value = HeroCoreStatTypes.getStatValue(ref, HeroCoreStatTypes.getSpellPower());

拦截伤害管线

使用 SystemDependency 注册一个 EntityEventSystem,将你的逻辑插入现有阶段之间:

@Override
protected void setup() {
    var reg = getEntityStoreRegistry();

    reg.registerSystem(new EntityEventSystem<EntityStore, HeroCoreDamageEvent>(HeroCoreDamageEvent.class) {
        @Override
        public Query<EntityStore> getQuery() {
            // 返回一个匹配拥有 HeroCoreStatsComponent 的实体的查询
            return HeroCoreStatsComponent.getComponentType();
        }

        @Override
        public Set<Dependency<EntityStore>> getDependencies() {
            return Set.of(
                new SystemDependency<>(Order.AFTER,  CriticalHitSystem.class),
                new SystemDependency<>(Order.BEFORE, LifestealSystem.class)
            );
        }

        @Override
        protected void handle(int index, ArchetypeChunk<EntityStore> chunk,
                              Store<EntityStore> store, CommandBuffer<EntityStore> cb,
                              HeroCoreDamageEvent event) {
            if (event.isCancelled()) return;
            // 你的自定义伤害修改逻辑在此
            // 例如:特定职业的伤害加成、元素交互
        }
    });
}

响应升级

reg.registerSystem(new EntityEventSystem<EntityStore, LevelUpEvent>(LevelUpEvent.class) {
    @Override
    public Query<EntityStore> getQuery() {
        return HeroCoreStatsComponent.getComponentType();
    }

    @Override
    protected void handle(int index, ArchetypeChunk<EntityStore> chunk,
                          Store<EntityStore> store, CommandBuffer<EntityStore> cb,
                          LevelUpEvent event) {
        HeroCoreStatsComponent rpg = chunk.getComponent(index, HeroCoreStatsComponent.getComponentType());
        // 在升级时缩放属性——派生系统将在下一滴答中获取更改
        rpg.setVitality(20f + event.getNewLevel() * 2f);
        rpg.setStrength(15f + event.getNewLevel() * 1.5f);
    }
});

响应战斗退出

reg.registerSystem(new EntityEventSystem<EntityStore, CombatExitEvent>(CombatExitEvent.class) {
    @Override
    public Query<EntityStore> getQuery() {
        // 匹配拥有 CombatStateComponent 的实体
        return CombatStateComponent.getComponentType();
    }

    @Override
    protected void handle(int index, ArchetypeChunk<EntityStore> chunk,
                          Store<EntityStore> store, CommandBuffer<EntityStore> cb,
                          CombatExitEvent event) {
        // 实体离开战斗——触发脱离战斗效果、UI 变更等
    }
});

修改主要属性

直接写入 HeroCoreStatsComponentAttributeDerivationSystem 会在下一个滴答自动重新计算派生修饰器:

HeroCoreStatsComponent stats = store.getComponent(ref, HeroCoreStatsComponent.getComponentType());
stats.setStrength(stats.getStrength() + 5f);
// 下一滴答:AttributeDerivationSystem 将更新后的修饰器写入 EntityStatMap

写入自定义修饰器

使用 HeroCoreModifiers 键约定以确保命名空间安全:

EntityStatMap statMap = store.getComponent(ref, EntityStatsModule.get().getEntityStatMapComponentType());
int critIdx = HeroCoreStatTypes.getCritChance();

// 应用修饰器(使用你的插件前缀命名空间)
// Hytale API:putModifier(int statIndex, String key, Modifier modifier)
statMap.putModifier(critIdx, "myplugin:elixir_crit_boost",
    new StaticModifier(Modifier.ModifierTarget.MAX, StaticModifier.CalculationType.ADDITIVE, 0.1f));

// 稍后移除
// Hytale API:removeModifier(int statIndex, String key)
statMap.removeModifier(critIdx, "myplugin:elixir_crit_boost");

**关于 putModifier 参数顺序的说明: