我最近写了一篇" Double Jump"我的插件的代码,它也在成功跳转后取消了falldamage。
目前的问题是,完全删除了falldamage,我无法找到原因。问题似乎是onFall
事件以及.setAllowFlight(true);
中的onMove
。
package at.skyblock.events;
import java.util.ArrayList;
import java.util.HashMap;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Material;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.event.player.PlayerToggleFlightEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import at.skyblok.SnowflakeUtil;
public class MovementHandler implements Listener {
private SnowflakeUtil pl;
private ArrayList<Player> jumpers = new ArrayList<Player>();
private HashMap<Player, Integer> cooldownTime = new HashMap<Player, Integer>();
private HashMap<Player, BukkitRunnable> cooldownTask = new HashMap<Player, BukkitRunnable>();
public MovementHandler(SnowflakeUtil pl) {
this.pl = pl;
}
@EventHandler
public void onFall(EntityDamageEvent e) {
if (e.getEntity() instanceof Player) {
if (e.getCause().equals(DamageCause.FALL)) {
Player p = (Player) e.getEntity();
if (jumpers.contains(p)) {
e.setCancelled(true);
jumpers.remove(p);
}
}
}
}
@EventHandler
public void onMove(final PlayerMoveEvent event) {
if (cooldownTime.containsKey(event.getPlayer()))
return;
if (event.getPlayer().getGameMode() != GameMode.CREATIVE
&& event.getPlayer().getLocation().getBlock().getRelative(BlockFace.DOWN).getType() != Material.AIR) {
event.getPlayer().sendMessage("ready");
event.getPlayer().setAllowFlight(true);
cooldownTime.put(event.getPlayer(), 5);
cooldownTask.put(event.getPlayer(), new BukkitRunnable() {
public void run() {
cooldownTime.put(event.getPlayer(), cooldownTime.get(event.getPlayer()) - 1);
if (cooldownTime.get(event.getPlayer()) == 0) {
cooldownTime.remove(event.getPlayer());
cooldownTask.remove(event.getPlayer());
jumpers.remove(event.getPlayer());
cancel();
}
}
});
cooldownTask.get(event.getPlayer()).runTaskTimer(pl, 20, 20);
}
}
@EventHandler(priority = EventPriority.HIGH)
public void onDoubleJump(PlayerToggleFlightEvent e) {
Player p = e.getPlayer();
if (!p.getGameMode().equals(GameMode.CREATIVE)) {
e.setCancelled(true);
p.setFlying(false);
p.setAllowFlight(false);
String type = "";
if (p.getInventory().getArmorContents() != null) {
for (ItemStack is : p.getInventory().getArmorContents()) {
if (is.hasItemMeta()) {
if (is.getItemMeta().hasLore()) {
for (int i = 0; i < is.getItemMeta().getLore().size(); i++) {
if (ChatColor.stripColor(is.getItemMeta().getLore().get(i).toLowerCase())
.contains(ChatColor.stripColor("movement"))) {
String part = ChatColor.stripColor(is.getItemMeta().getLore().get(i));
type = SnowflakeUtil
.capitalize(part.replaceAll("Movement:", "").replaceAll("\\s", ""));
}
}
}
}
}
}
jumpers.add(p);
switch (type) {
case "Rocketjump":
p.setVelocity(p.getLocation().getDirection().multiply(1));
p.setVelocity(new Vector(p.getVelocity().getX(), 0.75D, p.getVelocity().getZ()));
break;
}
}
}
}
答案 0 :(得分:1)
这里的障碍是player.setAllowFlight(true)
让玩家免受坠落伤害,但不幸的是,这是由客户而不是服务器完成的。客户端不会让服务器知道他们完全失败,因此不会触发EntityDamageEvent
,我们无法取消服务器可能已经阻止的任何损害(服务器不检查玩家是否已经摔倒,而是依赖客户端告诉服务器何时跌倒。
然而,由于玩家需要能够在整个秋季期间切换飞行模式以便触发PlayerFlightToggleEvent
(如果他们还没有使用过他们的双跳),我们需要一种方法来检测在玩家即将着陆之前的那一刻,此时,如果玩家仍然没有使用他们的双跳能力,我们可以安全地假设他们已经决定不使用它并且正在下降&#34;定期&#34;因此应该受到跌倒伤害。如果我们禁用了一名玩家早于使用双跳的能力,我们就会创建一个(不必要的)权衡,在这种情况下,玩家在着陆之前(甚至更早)可能不会在瞬间翻倍跳跃。
通过检查播放器即将在PlayerMoveEvent
期间移动到的位置下方的块,我们可以预测&#34;球员是否在非空中区域登陆。在PlayerMoveEvent
实际发生之前,我们禁用飞行模式,以便玩家在移动完成后定期坠落伤害。代码看起来像这样:
// Inside your "PlayerMoveEvent" method
// If a player is not on the ground and has fallen more than two blocks
if (!((CraftPlayer) player).isOnGround() && player.getFallDistance() > 2) {
Location to = event.getTo().clone().subtract(0, 0.0001, 0); // Get the location they will be at next tick
if (to.getBlock().getType() != Material.AIR) { // If that block is not air
player.setAllowFlight(false); // Cancel their ability to fly so that they take regular fall damage
}
}
请注意,播放器实体的下降距离和onGround
值由客户端提供,并且可以使用自定义客户端进行欺骗,因此使用脚下方的块来检查它们是否已打开可能更为智能地面与否(虽然这也是出于各种其他原因,但有时候并没有找到正确的结果,无论球员是否真的在场,都比看上去复杂得多)。
你的代码还有一个提示:如果你想为某种能力创造某种冷静期,而不是创造一个相对资源昂贵的任务,倒计时每一个滴答,你可以把玩家和时间放在他们身上可以在地图内再次使用该功能。如果他们再次尝试使用该功能,您可以检查当前时间是否超过允许他们再次使用该功能的时间。例如:
private HashMap<UUID, Long> abilityCooldown = new HashMap<>();
public void onEvent(SomePlayerEvent event) {
// Player is trying to use some ability...
// It's usually safer to store IDs rather than names or player objects for various reasons
UUID id = event.getPlayer().getUniqueId();
if (!abilityCooldown.containsKey(id)) { // They are not in the map, so have never tried to use the ability yet
// Let them use the ability here...
int seconds = 5; // The amount of time to cool down for
// Put the player's ID and the time when they will be allowed to use the ability again (future)
abilityCooldown.put(id, System.currentTimeMillis() + 1000 * seconds);
} else {
// The time when they are allowed to use the ability again (we put this in the map when they used it last)
long time = abilityCooldown.get(id);
if (time > System.currentTimeMillis()) { // If that time is still in the future
// Do not allow them to use the ability (maybe send them a message)
} else {
// Let them use the ability here...
int seconds = 5; // The amount of time to cool down for
abilityCooldown.put(id, System.currentTimeMillis() + 1000 * seconds); // Update the time when they can use the ablity again
}
}
}
答案 1 :(得分:-2)
使用ex之后是否将其恢复为默认值:
return false;
功能?
包括这个权利,球员跳跃。
@EventHandler(priority = EventPriority.HIGH)
public void onFallDamage(EntityDamageEvent event){
if(event.getEntity() instanceof Player){
Player player = (Player)event.getEntity();
if(event.getCause()){
event.setCancelled(true);
}
}
}