Skip to content

_Tutorial Coding Skills

EntityParrot_ edited this page Jun 26, 2020 · 2 revisions

第 1 步 - 继承 Skill 类

起步工作, 您需要创建一个继承了 Skill 类的 Java 类. 这是一个由 SkillAPI 提供的技能类的基础, 类似于一个技能的模板. 您的类应该看起来是这个样子的:

public class MySkill extends Skill

第 2 步 - 类构造器

技能类有四种不同的构造器供您使用. 取决于您希望您的技能是什么类型的. 四种选项分别为:

// 该构造器用于创建一个基础的技能带有默认的显示图标. 
//
// 参数 "name" 是您的技能名字. 将被用于通过命令施法, 绑定技能, 抑或是在提示信息或图标中展示出来.
//
// 参数 "type" 只是您要用来对技能进行分类的标签文本. 简单的示例: "AOE 范围法术(AOE Utility)" 或 "单体法术(Single Target)"
//
// 参数 "indicator" 是您想要用来代表您技能的图标的物品类型. 将在技能树 GUI 中展示.
//
// 参数 "maxLevel" 是您技能的最大等级. 
Skill(String name, String type, Material indicator, int maxLevel);

// 该构造器与上一个基本一致, 除了在升级该技能需要解锁另外一个技能及达到指定等级作为前置条件.
//
// 参数 "skillReq" 是前置技能的技能名.
//
// 参数 "skillReqLevel" 是前置技能所需达到的等级
Skill(String name, String type, Material indicator, int maxLevel, String skillReq, int skillReqLevel);

// 该构造器与第一个构造器一致, 除了参数 indicator 可以携带额外的数据
// 例如 MaterialData 或耐久值, 以自定义化您的技能图标.
// 也可以带有自定义 Lore 或者物品显示名以覆盖默认的技能图标物品的内容.
Skill(String name, String type, ItemStack indicator, int maxLevel);

// 该构造器与上一个类似, 但是与第二个一样, 有一个前置需求.
Skill(String name, String type, ItemStack indicator, int maxLevel, String skillReq, int skillReqLevel);

第 3 步 - 简介

您可以为技能设置一个将会在技能图标中显示的简介. 若要添加简介内容, 调用以下代码:

// 您想添加多少行简介, 就可以添加多少行.
// 建议平均每行简介保持在 20-30 字符之间.
getDescription().add("line of text");

第 4 步 - 属性

您可以设置技能的属性, 以控制内置的技能设置例如冷却时间, 技能点花费, 法力值消耗, 或等级需求. 也可以设置值以用于机制中. 您设置的所有属性都可以自动配置. 以下是设置方法:

// 基础方法结构:
// settings.set(String key, double base, double scale);
// settings.set(String key, Object value);
//
// 参数 "base" 是技能等级为 1 时的初始值
// 参数 "scale" 是技能每次升级时, 值所增长的大小.
//
// 仅填入一个参数即可存储不会变动的数据值, 例如字符串, 数值等.

// 技能的等级需求. 玩家必须达到指定等级才能解锁/升级该技能.
settings.set(SkillAttribute.LEVEL, 1, 1);

// 决定了需要多少技能点才能解锁/升级该技能
settings.set(SkillAttribute.COST, 1, 0);

// 玩家在施法一次后需要等待多久才能再次施法
settings.set(SkillAttribute.COOLDOWN, 10, -1);

// 技能的法力值消耗. 将会在施法成功后扣除指定的法力值.
// 若玩家没有足够的法力值, 将无法施法该技能.
// 若法力值系统被禁用, 该配置项没有任何意义.
settings.set(SkillAttribute.MANA, 10, 2);

// 您可以使用字符串作为ID, 来存储任何您想要存储的属性值.
settings.set("Damage", 5, 2);
settings.set("Duration", 3, 1);

第 5 步 - 实现一个技能类型接口

您的技能类应当实现三个可用接口中的一个以将其与 API 中的其他技能类保持一致. 只要您想, 您可以实现多个接口, 尽管大体来讲这是没有必要的. 三个可用的接口分别为:

SkillShot

// 代表无需直接目标即可施法的技能类型接口.
// 常见的应用包含发射投掷物, 自我施法类技能, 以及围绕施法者周围或施法者所看向的地区的 AOE 技能.
public interface SkillShot {
 
    // 执行不带有明确目标的施法动作的代码. 该方法适用于不带有目标的技能, 
    // 例如应用于您自身的技能, 围绕您自身的 AOE 范围技能, 或者其他不需要直接目标的技能.
    // 该方法提供了一个施法者及其施法时的技能等级作为可用参数.
    public boolean cast(LivingEntity user, int level);
}

TargetSkill

// 代表需要一个明确目标的技能类型接口.
// 常见应用包括召唤落雷攻击一个敌人,
// 对目标使用电击, 交换位置, 或其他效果.
public interface TargetSkill {

    // 执行当目标被指定时的施法动作的代码.
    // 该方法提供了一个施法者, 技能等级, 施法目标, 以及施法目标是否为施法者的盟友作为可用参数.
    public boolean cast(LivingEntity user, LivingEntity target, int level, boolean ally);
}

PassiveSkill

// 该接口所代表的技能没有施法的能力,
// 但是给予了方法来(在玩家获得技能时)启动, (在玩家失去技能, 死亡, 或下线时)结束技能效果.
public interface PassiveSkill {

    // 在玩家升级技能时发起更新技能效果内容的请求.
    // 大体上来讲您应该在这里清除旧的技能效果并应用一个新的(如果是一个持续的增益). 
    // 若您只是在获取每个程序的数据值,
    // 您可能不需要在更新技能效果时做任何事情.
    public void update(LivingEntity user, int prevLevel, int newLevel);

    // 该方法将在玩家激活该技能时执行. 例如玩家上线, 重生, 或刚解锁该技能.
    // 您可能需要将该玩家添加到一个数组或表中以标记其该技能处于激活状态,
    // 或给予其一个永久的增益, 直到被通知需要移除它.
    public void initialize(LivingEntity user, int level);

    // 该方法将在需要清除该玩家的被动效果时执行, 将其从列表中移除, 或是移除其身上的增益.
    // 将在玩家死亡, 下线, 或降级该技能至等级 0 时执行.
    public void stopEffects(LivingEntity user, int level);
}

Step 6 - 事件监听

若您需要在您的技能中监听事件, 只需要实现 Bukkit 的监听器(Listener)类接口, 然后 SkillAPI 将会自动处理并注册您的监听器(EventHandler).

示例技能

import com.rit.sucy.player.Protection;
import com.sucy.skill.api.skills.Skill;
import com.sucy.skill.api.skills.SkillAttribute;
import com.sucy.skill.api.skills.SkillShot;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.entity.ThrownPotion;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.PotionSplashEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.potion.Potion;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.potion.PotionType;

public class HealthPotion extends Skill implements SkillShot, Listener {

    public static final String NAME = "Health Potion";
    private static final String
            HEAL_AMOUNT = "Heal Amount",
            BONUS_DURATION = "Bonus Duration";

    public HealthPotion() {
        super(NAME, "AOE Heal", Material.GOLDEN_APPLE, 5);

        getDescription().add("Launches a potion that");
        getDescription().add("heals allies over time");
        getDescription().add("and grants bonus health");
        getDescription().add("for a short time.");

        settings.set(SkillAttribute.LEVEL, 1, 1);
        settings.set(SkillAttribute.COST, 1, 0);
        settings.set(SkillAttribute.COOLDOWN, 12, -1);
        settings.set(SkillAttribute.MANA, 18, -1);

        settings.set(HEAL_AMOUNT, 6, 2);
        settings.set(BONUS_DURATION, 6, 2);
    }

    @Override
    public boolean cast(LivingEntity user, int level) {

        Potion potion = new Potion(PotionType.REGEN, 1);
        potion.setSplash(true);

        ItemStack item = new ItemStack(Material.POTION);
        potion.apply(item);

        ThrownPotion thrownPotion = user.launchProjectile(ThrownPotion.class);
        thrownPotion.setItem(item);

        Plugin plugin = Bukkit.getPluginManager().getPlugin("SkillAPI");
        thrownPotion.setMetadata(NAME, new FixedMetadataValue(plugin, level));

        return true;
    }

    @EventHandler
    public void onPotionSplash(PotionSplashEvent event) {
        if (event.getEntity().hasMetadata(NAME)) {
            int level = event.getEntity().getMetadata(NAME).get(0).asInt();
            int regenDuration = (int)(12 * settings.getAttr(HEAL_AMOUNT, level));
            int healthDuration = (int)(20 * settings.getAttr(BONUS_DURATION, level));

            PotionEffect healEffect = new PotionEffect(PotionEffectType.REGENERATION, regenDuration, 2);
            PotionEffect healthEffect = new PotionEffect(PotionEffectType.HEALTH_BOOST, healthDuration, 0);
            for (LivingEntity entity : event.getAffectedEntities()) {
                if (Protection.isAlly((Player) event.getEntity().getShooter(), entity)) {
                    entity.addPotionEffect(healEffect);
                    entity.addPotionEffect(healthEffect);
                }
            }

            event.getAffectedEntities().clear();
        }
    }
}
Clone this wiki locally