11自定义事件
需要完成的功能:当玩家潜行状态攻击羊,可以使羊被击飞
自定义事件:
/**
* 自定义事件
* 当玩家潜行时伤害了一只羊事件。
* 自定义事件需要配合监听器或是在其他代码里,
* 用{@link org.bukkit.plugin.PluginManager#callEvent(Event)}手动触发该事件
*/
public class PlayerSneakingHitSheepEvent extends Event implements Cancellable {
/**
* 服务器中该事件的处理器集合,
* 我们只需要实现{@link Event#getHandlers()}然后返回这个对象就好,
* 具体的赋值会被服务端处理。
*/
private static final HandlerList handlers = new HandlerList();
/**
* 事件状态
*/
private boolean isCancelled;
/**
* 玩家
*/
private Player player;
/**
* 羊
*/
private Sheep sheep;
public PlayerSneakingHitSheepEvent(Player player, Sheep sheep) {
this.player = player;
this.sheep = sheep;
}
/**
* 一个是需要实现的getHandlers
*/
@Override
public HandlerList getHandlers() {
return handlers;
}
/**
* 一个是约定的getHandlerList
*/
public static HandlerList getHandlerList() {
return handlers;
}
@Override
public boolean isCancelled() {
return isCancelled;
}
public boolean nonCancelled() {
return isCancelled == false;
}
@Override
public void setCancelled(boolean cancel) {
this.isCancelled = cancel;
}
public Player getPlayer() {
return player;
}
public Sheep getSheep() {
return sheep;
}
}
触发器:
/**
* 玩家蹲下时伤害小样事件 触发器
* 如何知道 玩家在蹲下来的时候它是否有在击打某只羊?
* 别无他法,我们只能通过已有的API Event创建一个 监听链,
* 通过中间值来暂存 “状态” 信息,然后在需要的时候进行判断。
* 此时要格外注意,如果是用容器这类的数据结构来存放这些中间值,就必须考虑在什么时候去清除被存放的元素,
* 否则会存在内存泄漏的风险。
*/
public class PlayerSneakingHitSheepEventCaller implements Listener {
/**
* 正在蹲着的玩家
* <p>
* 状态信息(标记容器)
*/
private Set<UUID> playerSet = new HashSet<>();
/**
* 监听链第一步:玩家是否正在蹲着
*/
@EventHandler
public void onPlayerSneakingEvent(PlayerToggleSneakEvent event) {
if (event.isSneaking()) {
// 正在潜行时
playerSet.add(event.getPlayer().getUniqueId());
} else {
// 不在潜行就尝试删除中间状态
playerSet.remove(event.getPlayer().getUniqueId());
}
}
/**
* 监听链第二步:玩家有没有在蹲着的情况下伤害小羊
*/
@EventHandler
public void onPlayerDamageSheepEvent(EntityDamageByEntityEvent event) {
// 以前好像遇到过从事件中获取的对象为空的情况,于是我就先空判断了
Entity damager = event.getDamager();
Entity entity = event.getEntity();
// 如果damager存在、damger是玩家对象,被害者是羊
if (Objects.nonNull(damager) &&
damager instanceof Player &&
entity instanceof Sheep) {
// 通过中间值验证玩家是否在蹲着
if (playerSet.contains(damager.getUniqueId())) {
// 取消事件,不对小羊造成伤害
event.setCancelled(true);
// 触发我们的自定义事件,并清空中间状态信息
Bukkit.getPluginManager().callEvent(new PlayerSneakingHitSheepEvent((Player) damager, (Sheep) entity));
/**
* 一般情况下,都是在监听链的最后才remove | reset状态值,
* 我这里需要玩家在蹲着的时候能重复触发,因此选择在非蹲起的情况下remove | reset状态值。
* 还是那句话,小心内存泄漏!
*/
// playerSet.remove(damager.getUniqueId());
}
}
}
@EventHandler
public void onPlayerLogoutEvent(PlayerQuitEvent event) {
// 玩家退出时也删除标记
playerSet.remove(event.getPlayer().getUniqueId());
}
}
事件监听器:
/**
* 事件监听器
* 当玩家在潜行时击打小羊,小样就会垂直起飞、变换羊毛色彩、并给玩家一个音效提示.
* 确实是在模仿Hyoixel,但是我这没有给羊监听摔落伤害事件(
*/
public class OnPlayerSneakingHitSheepEventListener implements Listener {
@EventHandler
public void onPlayerSneakingHitSheepEventListener(PlayerSneakingHitSheepEvent event) {
Player player = event.getPlayer();
Sheep sheep = event.getSheep();
Random random = new Random();
int next = random.nextInt(DyeColor.values().length); // [0, length) 左闭右开的int
// 随机颜色
sheep.setColor(DyeColor.values()[next]);
// 垂直起飞:施加Velocity速度,速度矢量为y轴方向一个单位
sheep.setVelocity(new Vector(0, 1, 0));
// 音效:玩家#playSound和word#playSound是有区别的,一个是只给该玩家发送音效封包,另一个是给全体玩家发送音效封包
player.playSound(player.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 1F, 1F);
}
}
效果: