Skip to content

BedWars 游戏事件系统

概述

游戏事件系统(Game Event System)管理 BedWars 游戏中的定时事件链,如产矿机升级、床自毁、末影龙出没、游戏结束等。事件按配置文件中的顺序依次执行倒计时,倒计时结束后触发事件动作,然后开始下一个事件的倒计时。

核心设计原则:

  • 配置驱动:所有事件数据通过 YAML 配置文件定义,不包含硬编码数值
  • 模式支持:通过 mode-mapping 机制为不同游戏模式加载不同事件配置
  • 可扩展:新增事件类型只需实现 EventAction 接口并在 EventManager 中注册
  • 生命周期联动:事件系统随游戏启动而创建,随游戏结束而销毁

架构对标产矿机系统,采用相同的独立配置文件 + 模式映射模式。


默认事件序列

在默认的 4 队模式中,事件按以下顺序依次执行:

序号事件 ID显示名类型倒计时效果
1diamond_upgrade_2钻石点IIgenerator_upgrade6 分钟钻石产矿机升级为 2 级
2emerald_upgrade_2绿宝石点IIgenerator_upgrade6 分钟绿宝石产矿机升级为 2 级
3diamond_upgrade_3钻石点IIIgenerator_upgrade6 分钟钻石产矿机升级为 3 级
4emerald_upgrade_3绿宝石点IIIgenerator_upgrade6 分钟绿宝石产矿机升级为 3 级
5bed_destroy床自毁bed_destroy6 分钟所有存活队伍的床自动破坏
6dragon_spawn末影龙出没dragon_spawn6 分钟每个存活队伍出生点上方生成末影龙
7game_end游戏结束game_end6 分钟强制平局结束游戏

每个事件的倒计时是独立计算的上文倒计时,不是从游戏开始累计。上一个事件执行完毕后才开始下一个事件的倒计时。


配置文件

目录结构

plugins/BedWars/
└── events/
    └── default.yml      # 默认配置(所有模式未单独指定时使用)
    # 可按需添加更多模式配置,如 fast.yml

配置文件在插件启动时通过 saveResource("events/default.yml", false) 自动释放。

配置文件格式

default.yml 为例:

yaml
# 模式映射:模式ID → 配置文件名(不含 .yml 后缀)
# 未映射的模式使用 default
mode-mapping:
  4t: "default"
  # fast: "fast"    # 示例:快节奏模式使用 fast.yml

# 事件序列(有序列表,按顺序依次执行倒计时)
# countdown 单位:秒
# display-name-key:i18n key,用于获取显示名称
events:
  - id: "diamond_upgrade_2"
    display-name-key: "bedwars-event-diamond-point-2"
    type: "generator_upgrade"
    countdown: 360
    params:
      generator-type: "diamond_generator"
      upgrade-level: 2

  - id: "emerald_upgrade_2"
    display-name-key: "bedwars-event-emerald-point-2"
    type: "generator_upgrade"
    countdown: 360
    params:
      generator-type: "emerald_generator"
      upgrade-level: 2

  # ... 更多事件

  - id: "game_end"
    display-name-key: "bedwars-event-game-end"
    type: "game_end"
    countdown: 360

事件字段说明

字段类型必填说明
idString事件唯一标识符
display-name-keyStringi18n key,用于获取事件显示名称(非硬编码文本)
typeString事件类型,对应 EventAction 实现类
countdownint倒计时秒数,上一个事件执行后开始倒计时
paramsMap扩展参数,不同事件类型有不同参数

模式配置映射

与产矿机系统完全相同的模式映射机制:

yaml
mode-mapping:
  4t: "default"    # 4 队模式使用 default.yml
  fast: "fast"     # fast 模式使用 fast.yml

重要mode-mapping 可以定义在任意配置文件中,系统启动时扫描 events/ 目录下所有 .yml 文件收集映射。

加载逻辑:

  1. 启动时扫描所有 events/*.yml,收集所有 mode-mapping
  2. EventConfig.loadForMode(modeId) 查找已收集的映射
  3. 找到映射 → 加载对应文件;未找到 → 使用 default
  4. 配置文件不存在 → 自动降级到 default
  5. 已加载的配置会被缓存,不会重复解析

核心类

包结构

cn.enderrealm.bedwars.game.event/
  ├── EventConfig.java          # 配置加载器
  ├── EventData.java            # 事件数据模型
  ├── EventAction.java          # 事件动作接口
  ├── EventScheduler.java       # 事件链调度器
  ├── EventManager.java         # 生命周期管理
  ├── GeneratorUpgradeAction.java  # 产矿机升级
  ├── BedDestroyAction.java     # 床自毁
  ├── DragonSpawnAction.java    # 末影龙生成
  └── GameEndAction.java        # 游戏结束

cn.enderrealm.bedwars.entity/
  └── DragonManager.java        # 末影龙实体管理

类关系图

GameMode.setupEvents()
  → new EventManager(plugin, gameManager)
  → eventManager.setupEvents()
      → config.loadForMode(modeId)           // 加载配置
      → new EventScheduler(...)               // 创建调度器
      → scheduler.start()                     // 启动倒计时链

EventScheduler.run() (每秒 tick)
  → countdownRemaining--
  → countdownRemaining <= 0?
      → eventManager.getAction(type)          // 获取动作
      → action.execute(gameManager, eventData) // 执行动作
      → currentIndex++                         // 切换下一个事件
      → 所有事件完成 → scheduler.cancel()

GameManager.cleanup()
  → eventManager.stop()
      → scheduler.stop()
      → action.cleanup() (所有动作)
      → DragonManager.removeAll()

EventConfig

cn.enderrealm.bedwars.game.event.EventConfig

事件配置管理器,从 YAML 文件动态加载事件序列。与 GeneratorConfig 同构。

方法说明
loadForMode(modeId)根据模式 ID 加载对应配置文件
getActiveEvents()获取当前活跃的事件序列(只读)
getActiveConfigName()当前使用的配置文件名
getModeMapping()获取模式映射表(只读)

EventData

cn.enderrealm.bedwars.game.event.EventData

单个事件的纯数据模型,从配置文件加载。

字段/方法类型说明
idString事件唯一标识
displayNameKeyStringi18n key(如 bedwars-event-diamond-point-2
typeString事件类型(如 generator_upgrade
countdownint倒计时秒数
paramsMap<String, Object>扩展参数
getStringParam(key)String获取字符串参数
getIntParam(key, default)int获取整数参数

EventAction

cn.enderrealm.bedwars.game.event.EventAction (接口)

事件动作接口,每种事件类型实现此接口。

方法说明
execute(GameManager, EventData)执行事件逻辑
cleanup()游戏结束时清理资源

EventScheduler

cn.enderrealm.bedwars.game.event.EventScheduler

事件链调度器,继承 BukkitRunnable,每秒 tick 一次管理倒计时。

方法说明
start()启动调度器,开始第一个事件的倒计时
stop()停止调度器,取消所有倒计时
getCurrentEvent()获取当前倒计时中的事件
getCurrentEventIndex()当前事件索引,-1 表示未开始或已结束
getCountdownRemaining()当前事件剩余秒数
getCurrentEventId()当前事件 ID
getCurrentEventNameKey()当前事件显示名 i18n key
isRunning()调度器是否运行中

getCurrentEvent()getCountdownRemaining() 等查询方法设计为供外部系统(如计分板、Sidebar)调用,事件系统本身不做倒计时广播。

EventManager

cn.enderrealm.bedwars.game.event.EventManager

事件生命周期管理器,等价于产矿机系统中的 GeneratorManager

方法说明
setupEvents()初始化并启动事件系统
stop()停止并清理事件系统(游戏结束时调用)
registerAction(type, action)注册事件类型
getAction(type)获取事件动作实现
getCurrentEvent()代理给调度器的查询方法
getCountdownRemaining()代理给调度器的查询方法

内置注册的事件类型:

type实现类说明
generator_upgradeGeneratorUpgradeAction产矿机升级
bed_destroyBedDestroyAction床自毁
dragon_spawnDragonSpawnAction末影龙生成
game_endGameEndAction游戏结束

事件类型详解

产矿机升级 (generator_upgrade)

调用现有的 GeneratorManager.upgradeAllByType() 方法升级指定类型的所有产矿机。

参数类型说明
generator-typeString产矿机类型,如 "diamond_generator""emerald_generator"
upgrade-levelint目标等级,如 2 或 3
java
// 等效于
gameManager.getGeneratorManager().upgradeAllByType("diamond_generator", 2);

床自毁 (bed_destroy)

强制破坏所有存活队伍的床,触发与手动破坏相同的正反馈效果:

  1. 移除床方块:检查床位置的方块,设置 AIR
  2. 更新状态gameManager.breakBed(teamId)
  3. 正反馈:对每个玩家发送与 BedBreakTask 完全相同的 Title、Sound、Chat 消息
    • 己方:红色 Title + 凋零声音
    • 敌方:队伍名 Title + 末影龙嘶吼声音
  4. 额外全局通知:发送 bedwars-event-bed-auto-destroy 消息通知全房间

与玩家手动破坏的唯一区别:没有"破坏者",反馈中不包含玩家名信息。

末影龙生成 (dragon_spawn)

在每个存活队伍的出生点 Y+20 位置生成末影龙。

参数类型默认值说明
levelint1每队生成的龙数量

末影龙行为:

  • 持续飞行:默认 Phase.CIRCLING,不主动攻击玩家
  • 定时冲撞:每 15 秒短暂切换到 Phase.CHARGE_PLAYER,3 秒后恢复盘旋
  • 偏向建筑物:冲撞行为使龙周期性靠近建筑
  • 方块破坏:监听 EntityExplodeEvent,仅处理被追踪的龙
    • 20% 方块 → FallingBlock(向四周喷射,5 秒后自动移除)
    • 80% 方块 → 直接消失(不掉落物)

DragonManager 提供 upgradeDragonLevel() 公开 API,预留给未来商店系统调用,可以给队伍增加额外的龙。

游戏结束 (game_end)

触发平局结束逻辑,与 GameCheckTask 的流程相同:

  1. 检查 gameManager.isGameEnding() → 如果已经结束则跳过(防止重复触发)
  2. 设置 gameManager.setGameEnding(true)
  3. 取消所有重生任务
  4. 执行 new GameEndTask(plugin, gameManager, true, null).runTask(plugin)(平局)

如果游戏在事件序列中途因 GameCheckTask 自然结束(只剩 1 队),事件系统会随 GameManager.cleanup() 被销毁,后续事件不会执行。


末影龙管理器

DragonManager

cn.enderrealm.bedwars.entity.DragonManager

末影龙实体管理器,独立于事件系统所在的包(位于 entity 包下)。

方法说明
spawnDragon(loc, teamId)在指定位置生成末影龙,返回龙实体
upgradeDragonLevel(gm, newLevel)升级每队龙数量(API,预留给商店系统)
isTrackedDragon(entity)检查实体是否为被追踪的末影龙
removeAll()清除所有龙实体
cleanup()清理资源(移除龙 + 注销监听器)
getDragonCount()当前存活龙数量
getCurrentLevel()当前龙等级

游戏流程中的事件系统

生命周期

游戏开始
  → GameInitTask 步骤 8
    → GameMode.setupEvents(gameManager)     ← 模式钩子
      → 默认: new EventManager(plugin, gm)
      → eventManager.setupEvents()
          → config.loadForMode(modeId)      // 加载事件配置
          → scheduler.start()               // 开始倒计时链

游戏进行中
  → EventScheduler 每秒 tick
  → 倒计时归零 → 执行 EventAction.execute()
  → 所有事件完成 → scheduler 自动结束

任意时刻游戏自然结束(GameCheckTask 检测到 ≤1 队存活)
  → GameManager.cleanup()
    → eventManager.stop()
        → scheduler.cancel()
        → 所有 EventAction.cleanup()
        → DragonManager.removeAll()         // 移除所有末影龙

启动时机

事件系统在游戏初始化任务链的第 8 步启动(在 initGameData() 之前),确保玩家位置、产矿机、游戏状态均已完成设置。

GameInitTask 执行顺序:
  1. LoadTeamLocationsTask  [异步]
  2. AssignTeamsTask         [异步]
  3. SetupPlayerTeamsTask    [同步]
  4. InitTeamBedsTask        [同步]
  5. TeleportPlayersTask     [同步]
  6. setupGenerators + setupShops [同步]
  7. onGameStart             [同步]
  8. setupEvents             [同步]  ← 事件系统在此启动
  9. initGameData            [同步]

i18n Keys

所有事件显示名称通过 display-name-key 引用 i18n key,不在代码中硬编码文本。

Key中文English
bedwars-event-diamond-point-2§b钻石点II§bDiamond Point II
bedwars-event-diamond-point-3§b钻石点III§bDiamond Point III
bedwars-event-emerald-point-2§a绿宝石点II§aEmerald Point II
bedwars-event-emerald-point-3§a绿宝石点III§aEmerald Point III
bedwars-event-bed-self-destruct§c床自毁§cBed Self-Destruct
bedwars-event-dragon-spawn§5末影龙出没§5Dragon Incoming
bedwars-event-game-end§c游戏结束§cGame End
bedwars-event-bed-auto-destroy§c所有队伍的床已自动毁灭!§cAll beds have been automatically destroyed!
bedwars-event-dragon-name§5末影龙§5Ender Dragon

扩展指南

添加新事件类型

  1. 创建 EventAction 实现类:
java
public class MyCustomAction implements EventAction {
    @Override
    public void execute(GameManager gameManager, EventData eventData) {
        // 事件逻辑
    }

    @Override
    public void cleanup() {
        // 清理资源
    }
}
  1. EventManager.registerBuiltinActions() 中注册:
java
registerAction("my_custom_type", new MyCustomAction());
  1. 在配置文件中添加事件:
yaml
events:
  - id: "my_custom_event"
    display-name-key: "bedwars-event-my-custom"
    type: "my_custom_type"
    countdown: 180
    params:
      custom-param: "value"
  1. 在语言文件中添加 bedwars-event-my-custom key。

添加新模式专属事件配置

  1. events/ 目录下创建新配置文件(如 fast.yml
  2. 在新配置文件中添加 mode-mapping 段:
yaml
# fast.yml
mode-mapping:
  fast: "fast"

events:
  - id: "diamond_upgrade_2"
    display-name-key: "bedwars-event-diamond-point-2"
    type: "generator_upgrade"
    countdown: 180    # 更快的节奏
    params:
      generator-type: "diamond_generator"
      upgrade-level: 2
  # ...
  1. 系统启动时会自动扫描所有配置文件,收集 mode-mapping
  2. 无需修改代码

通过 GameMode 钩子定制事件

模式可以覆写 setupEvents() 来自定义事件行为:

java
public class CustomMode extends GameMode {
    // ...

    @Override
    public void setupEvents(GameManager gm) {
        BedWars plugin = gm.getPlugin();
        EventManager eventManager = new EventManager(plugin, gm);

        // 注册自定义事件类型
        eventManager.registerAction("my_custom_type", new MyCustomAction());

        eventManager.setupEvents();
        gm.setEventManager(eventManager);
    }
}

外部系统获取事件信息

计分板或 Sidebar 可以通过 EventManager 的查询方法获取当前事件状态:

java
EventManager eventManager = gameManager.getEventManager();
if (eventManager != null) {
    EventData current = eventManager.getCurrentEvent();
    int remaining = eventManager.getCountdownRemaining();

    if (current != null) {
        String nameKey = current.getDisplayNameKey();
        String displayName = plugin.getLanguageManager().getText(nameKey, player);
        // 显示: "下一次事件: 钻石点II (还有 120 秒)"
    }
}