2016-02-14 5 views
0

Недавно я написал код «Double Jump» для моего плагина, который также отменяет falldamage после успешного прыжка.Minecraft (Bukkit): Проблема с falldamage после «Doublejump»

Текущая проблема в том, что 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 (ложь), не имеет никакого смысла, и это событие должно работать нормально, когда игрок * не * в списке – AscendedKitten

+0

Ваш код не компилируется, 'event.getCause' не возвращает логическое значение, а' EntityDamageEvent.DamageCause' – Ferrybig

+1

Вот что я тоже подумал. if (e.getCause(). равно (DamageCause.FALL)) должен быть правильным синтаксисом в этом случае – AscendedKitten

Смежные вопросы