天堂2单机论坛

 找回密码
 立即注册
查看: 177|回复: 5

l2s 脚本教学之【事件监听脚本】

[复制链接]
  • TA的每日心情
    开心
    2024-3-5 07:47
  • 签到天数: 1 天

    [LV.1]初来乍到

    3

    主题

    54

    帖子

    6177

    积分

    论坛元老

    Rank: 8Rank: 8

    积分
    6177
    发表于 2025-11-29 22:04:49 | 显示全部楼层 |阅读模式
    购买主题 已有 4 人购买  本主题需向作者支付 15 金钱 才能浏览
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2024-3-5 07:47
  • 签到天数: 1 天

    [LV.1]初来乍到

    3

    主题

    54

    帖子

    6177

    积分

    论坛元老

    Rank: 8Rank: 8

    积分
    6177
     楼主| 发表于 2025-11-29 22:19:18 | 显示全部楼层
    详解 BowVampire 类:弓箭职业吸血效果实现教程
    BowVampire 这个类 —— 它是一个游戏服务器中「弓箭职业吸血效果」的核心实现。通过这个案例,学习游戏脚本开发规范、事件监听机制、职业 / 装备判定逻辑 以及 数值计算设计,全程结合代码逐行拆解,让大家不仅懂「是什么」,更懂「为什么这么写」。

    一、类的核心定位与整体结构1. 核心功能这个类的作用是:给使用弓 / 弩的弓箭手职业玩家,添加「攻击吸血」效果—— 玩家攻击怪物或其他玩家时,根据造成的伤害按比例恢复自身生命值(HP)。


    2. 整体结构(4 大模块)
    1. BowVampire
    2. ├─ 接口实现:ScriptFile(游戏脚本生命周期管理)
    3. ├─ 工具方法:职业判定、武器判定、吸血数值计算
    4. ├─ 内部监听器:CurrentHpDamageListener(监听伤害事件)
    5. └─ 核心逻辑:伤害触发吸血的完整流程
    复制代码
    3. 依赖与前置知识

    • 游戏服务器核心类:Player(玩家)、Creature(生物,包含玩家 / 怪物)、Skill(技能)、Inventory(背包)等(由服务器框架提供);
    • 设计模式:观察者模式(通过监听器监听伤害事件);
    • 脚本规范:实现 ScriptFile 接口,让服务器能自动加载 / 重载脚本。


    二、第一步:脚本生命周期管理(ScriptFile 接口)首先看类的开头:public class BowVampire implements ScriptFile。这是游戏脚本的标准写法,ScriptFile 是服务器提供的「脚本生命周期接口」,要求实现 3 个方法:

    1. 关键方法解析
    1. // 脚本加载时执行(服务器启动/脚本热加载时调用)
    2. @Override
    3. public void onLoad() {
    4.     // 给所有生物添加「生命值伤害监听器」
    5.     CharListenerList.addGlobal(new CurrentHpDamageListener());
    6. }

    7. // 脚本重载时执行(服务器不重启更新脚本时调用,这里暂未实现)
    8. @Override
    9. public void onReload() {}

    10. // 脚本关闭时执行(服务器关闭时调用,这里暂未实现)
    11. @Override
    12. public void onShutdown() {}
    复制代码


    2. 核心逻辑:监听器注册onLoad() 是整个类的「入口」—— 服务器加载脚本时,会通过 CharListenerList.addGlobal(...) 把我们自定义的 CurrentHpDamageListener(伤害监听器)注册为「全局监听器」。

    这意味着:游戏中任何生物(玩家 / 怪物)受到伤害时,都会触发我们监听器里的 onCurrentHpDamage 方法—— 这是吸血效果能「被动触发」的核心。

    三、第二步:工具方法 —— 职业、武器的判定逻辑要实现「只有弓箭手用弓 / 弩才能吸血」,首先需要两个关键判定:「是不是弓箭手」「是不是用弓 / 弩」。这部分是「过滤逻辑」,确保吸血效果只作用于目标玩家。

    1. 职业判定:isArcher (Player player)
    1. // 判断玩家是否为弓箭手职业
    2. protected boolean isArcher(Player player) {
    3.     // 按职业ID筛选(游戏中每个职业有唯一ID)
    4.     switch (player.getClassId().getId()) {
    5.         case 9:
    6.         case 24:
    7.         case 37:
    8.         // ... 其他弓箭手职业ID
    9.         case 134:
    10.             return true; // 是弓箭手,返回true
    11.         default:
    12.             return false; // 非弓箭手,返回false
    13.     }
    14. }
    复制代码
    • 核心思路:游戏中每个职业都有唯一的 classId,通过硬编码筛选所有弓箭手\弩等职业;


    2. 武器判定:weaponIsBow (Player actor)

    1. // 判断玩家是否装备了弓/弩
    2. public boolean weaponIsBow(Player actor) {
    3.     // 1. 获取玩家的背包(Inventory)
    4.     Inventory inventory = actor.getInventory();
    5.     // 2. 获取玩家装备栏第7格的物品(PaperdollItem:装备栏,7是武器栏位ID)
    6.     ItemInstance item = inventory.getPaperdollItem(7);
    7.     // 3. 判定物品类型:是否为弓(BOW)或弩(CROSSBOW)
    8.     if (item != null) {
    9.         return item.getItemType() == WeaponTemplate.WeaponType.BOW
    10.             || item.getItemType() == WeaponTemplate.WeaponType.CROSSBOW;
    11.     }
    12.     return false; // 未装备武器或装备的不是弓/弩
    13. }
    复制代码
    • 关键细节:
      • PaperdollItem(7):游戏装备栏的「武器栏位」;
      • WeaponTemplate.WeaponType:服务器定义的武器类型枚举,包含剑、弓、弩等,通过枚举判定避免字符串错误。


    3. 目标类型判定:getType (Creature creature)
    1. // 判断受伤害的目标是否是「可吸血的对象」(怪物或玩家)
    2. private boolean getType(Creature creature) {
    3.     return creature.isMonster() || creature.isPlayer();
    4. }
    复制代码
    • 逻辑:只有攻击「怪物」或「其他玩家」时才触发吸血,攻击 NPC、环境等不生效。

    四、第三步:内部监听器 —— 捕捉伤害事件(观察者模式核心)这是整个类的「核心触发模块」,CurrentHpDamageListener 实现了服务器提供的 OnCurrentHpDamageListener 接口,专门监听「生物受到生命值伤害」的事件。

    1. 监听器方法解析
    1. // 内部类:伤害监听器
    2. private class CurrentHpDamageListener implements OnCurrentHpDamageListener {

    3.     // 当有生物受到伤害时,服务器会自动调用这个方法
    4.     // creature:受伤害的目标(比如被攻击的怪物/玩家)
    5.     // v:造成的伤害值(核心数值,吸血比例基于这个计算)
    6.     // creature1:造成伤害的攻击者
    7.     // skill:使用的技能(若为普通攻击则为null)
    8.     @Override
    9.     public void onCurrentHpDamage(Creature creature, double v, Creature creature1, Skill skill) {

    10.         Player attacker = null;

    11.         // 过滤逻辑1:受伤害的目标必须是怪物或玩家(排除NPC等)
    12.         if(!getType(creature)){
    13.             return;
    14.         }

    15.         // 过滤逻辑2:攻击者必须是玩家(怪物攻击不触发吸血)
    16.         if (creature1 instanceof Player) {
    17.             attacker = creature1.getPlayer(); // 把Creature强转为Player
    18.         }
    19.         if (attacker == null) {
    20.             return; // 攻击者不是玩家(比如怪物攻击、环境伤害),直接退出
    21.         }

    22.         // 过滤逻辑3:排除「假人」
    23.         if (attacker.isFakePlayer()) {
    24.             return;
    25.         }

    26.         // 过滤逻辑4:攻击者必须是弓箭手职业
    27.         if (!isArcher(attacker)) {
    28.             return;
    29.         }

    30.         // 过滤逻辑5:攻击者必须装备弓/弩
    31.         if (!weaponIsBow(attacker)){
    32.             return;
    33.         }

    34.         // 过滤逻辑6:等级差限制(避免高等级玩家秒杀低等级目标吸血)
    35.         if(attacker.getLevel() - creature.getLevel() > 9){
    36.             return;
    37.         }

    38.         // 最终触发:伤害值>0时,计算并恢复HP
    39.         if (v > 0) {
    40.             calculateHP(attacker, v);
    41.         }
    42.     }
    43. }
    复制代码
    2. 过滤逻辑的设计思路(重点学习)这里的 6 个「过滤逻辑」是「逐步缩小范围」,确保吸血效果只在「符合所有条件」时触发,这是游戏开发中「避免异常触发」的标准写法:

    1. 触发吸血的条件 = 目标是怪物/玩家 + 攻击者是真人玩家 + 职业是弓箭手 + 装备弓/弩 + 等级差≤9 + 造成伤害>0
    复制代码
    • 为什么要这么写?比如:
      • 若不判断 isFakePlayer(),服务器机器人可能会无限吸血;
      • 若不判断等级差,高等级玩家秒杀低等级怪物会导致过度吸血,破坏游戏平衡。





    回复

    使用道具 举报

  • TA的每日心情
    无聊
    昨天 16:18
  • 签到天数: 66 天

    [LV.6]常住居民II

    4

    主题

    123

    帖子

    390

    积分

    中级会员

    Rank: 3Rank: 3

    积分
    390
    发表于 2025-11-30 13:37:55 | 显示全部楼层
    目标 被石化的情况 ?
    回复

    使用道具 举报

  • TA的每日心情
    擦汗
    3 天前
  • 签到天数: 55 天

    [LV.5]常住居民I

    2

    主题

    192

    帖子

    194

    积分

    正式会员

    Rank: 2

    积分
    194
    发表于 2025-11-30 23:25:24 | 显示全部楼层
    6666666666666666666
    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    前天 11:09
  • 签到天数: 598 天

    [LV.9]以坛为家II

    0

    主题

    673

    帖子

    2060

    积分

    金牌会员

    Rank: 6Rank: 6

    积分
    2060
    发表于 7 天前 | 显示全部楼层
    楼主辛苦了,感谢这么详细的详解.虽然看不懂,但看起来很厉害
    回复

    使用道具 举报

  • TA的每日心情
    擦汗
    6 小时前
  • 签到天数: 203 天

    [LV.7]常住居民III

    7

    主题

    367

    帖子

    1384

    积分

    金牌会员

    Rank: 6Rank: 6

    积分
    1384
    发表于 5 天前 | 显示全部楼层
    好贵啊,看一看教程先。
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|手机版|监狱|天堂2单机论坛 ( 粤ICP备20030366号 )

    GMT+8, 2025-12-8 06:56 , Processed in 0.063491 second(s), 24 queries .

    Powered by Discuz! X3.4

    © 2001-2023 Discuz! Team.

    快速回复 返回顶部 返回列表