2016-02-14 26 views
0

我最近爲我的插件寫了一個「Double Jump」代碼,它也取消了成功跳轉後的falldamage。Minecraft(Bukkit):在「Doublejump」後發佈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; 

      } 

     } 
    } 

} 

回答

1

的這裏的障礙是player.setAllowFlight(true)讓玩家免疫下跌的傷害,但不幸的是,這是由客戶端而不是服務器來完成。客戶端不讓服務器知道他們完全失敗,因此沒有任何EntityDamageEvent被觸發,我們也不能取消服務器可能已經阻止的任何損害(服務器不檢查球員是否已經倒下而是依靠客戶端告訴服務器何時下跌)。然而,由於玩家需要能夠在整個秋季切換飛行模式以便觸發PlayerFlightToggleEvent(如果他們還沒有使用雙跳),我們需要一種方法來檢測恰好在玩家之前的時刻即將降落,此時,如果球員還沒有使用雙跳能力,我們可以放心地認爲他們已經決定完全不使用它,並且「經常」下降,因此應該受到墜落傷害。如果我們禁止了一名球員使用雙跳的能力,我們會創建一個(不必要的)折衷方案,讓一名球員在着陸(或更早)之前一分鐘內不可能雙跳。

通過在PlayerMoveEvent期間檢查玩家即將移動到的位置下方的區塊,我們可以「預測」玩家是否處於非空中區塊的着陸行爲。在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 
     } 
    } 
} 
-2

你寄回來使用它除權後默認: 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); 
     } 
    } 
} 
+0

我不太清楚你想告訴我什麼... setCancelled(false)沒有任何意義,並且當玩家*不在列表中時,該事件應該正常工作 – AscendedKitten

+0

你的代碼不能編譯,'event.getCause'不會返回一個布爾值,而是一個'EntityDamageEvent.DamageCause' – Ferrybig

+1

這就是我的想法。如果(e.getCause()。equals(DamageCause。FALL))在這種情況下應該是正確的語法 – AscendedKitten