/******************************************************************************* * Copyright 2014 Tob * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package de.tobiyas.racesandclasses.traitcontainer.traits.defaultraits.activate.GrapplingHookTrait; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.Arrow; import org.bukkit.entity.Player; import org.bukkit.entity.Projectile; import org.bukkit.event.Event; import org.bukkit.event.EventHandler; import org.bukkit.event.block.Action; import org.bukkit.event.entity.ProjectileHitEvent; import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.scheduler.BukkitRunnable; import org.bukkit.util.Vector; import de.tobiyas.racesandclasses.eventprocessing.eventresolvage.EventWrapper; import de.tobiyas.racesandclasses.eventprocessing.eventresolvage.EventWrapperFactory; import de.tobiyas.racesandclasses.playermanagement.player.RaCPlayer; import de.tobiyas.racesandclasses.traitcontainer.interfaces.AbstractBasicTrait; import de.tobiyas.racesandclasses.traitcontainer.interfaces.TraitResults; import de.tobiyas.racesandclasses.traitcontainer.interfaces.annotations.configuration.TraitConfigurationField; import de.tobiyas.racesandclasses.traitcontainer.interfaces.annotations.configuration.TraitConfigurationNeeded; import de.tobiyas.racesandclasses.traitcontainer.interfaces.annotations.configuration.TraitEventsUsed; import de.tobiyas.racesandclasses.traitcontainer.interfaces.annotations.configuration.TraitInfos; import de.tobiyas.racesandclasses.traitcontainer.interfaces.markerinterfaces.Trait; import de.tobiyas.racesandclasses.util.traitutil.TraitConfiguration; import de.tobiyas.racesandclasses.util.traitutil.TraitConfigurationFailedException; import de.tobiyas.util.schedule.DebugBukkitRunnable; import de.tobiyas.util.vollotile.ParticleEffects; import de.tobiyas.util.vollotile.VollotileCodeManager; public class GrapplingHookTrait extends AbstractBasicTrait{ private Material materialToUse = Material.ARROW; private final Map<Projectile, UUID> launchMap = new HashMap<Projectile, UUID>(); @TraitEventsUsed(registerdClasses = {PlayerInteractEvent.class, ProjectileHitEvent.class}) @Override public void generalInit(){ plugin.registerEvents(this); } @Override public String getName() { return "GrapplingHookTrait"; } @Override protected String getPrettyConfigIntern() { return "fire a Grappling hook."; } @TraitConfigurationNeeded(fields = { @TraitConfigurationField(fieldName = "material", classToExpect = Material.class) }) @Override public void setConfiguration(TraitConfiguration configMap) throws TraitConfigurationFailedException { super.setConfiguration(configMap); materialToUse = configMap.getAsMaterial("material"); } @Override public TraitResults trigger(EventWrapper eventWrapper) { Event event = eventWrapper.getEvent(); if(event instanceof PlayerInteractEvent){ PlayerInteractEvent Eevent = (PlayerInteractEvent) event; Player player = Eevent.getPlayer(); //If pulling -> return. if(pulling.contains(player.getUniqueId())) return TraitResults.False(); Arrow projectile = player.launchProjectile(Arrow.class); launchMap.put(projectile, player.getUniqueId()); return TraitResults.True(); } if(event instanceof ProjectileHitEvent){ ProjectileHitEvent hitEvent = ((ProjectileHitEvent)event); Projectile projectile = hitEvent.getEntity(); Player player = Bukkit.getPlayer(launchMap.get(projectile)); launchMap.remove(projectile); projectile.remove(); //Just sanity Check. if(pulling.contains(player.getUniqueId())) return TraitResults.False(); //Only if online! if(player.isOnline()) pullPlayerTo(player, projectile.getLocation()); } return TraitResults.True(); } /** * The Set of pulling people. */ private Set<UUID> pulling = new HashSet<>(); /** * If currently teleporting. */ private boolean isGrapplingTeleport = false; @EventHandler public void playerMove(PlayerMoveEvent event){ if(isGrapplingTeleport) return; if(event.getFrom().distance(event.getTo()) < 0.1) return; Player player = event.getPlayer(); if(pulling.contains(player.getUniqueId())) { event.setCancelled(true); } } @EventHandler public void playerLeave(PlayerQuitEvent event){ Player pl = event.getPlayer(); //Small quickfix for Leaving players still have fly. if(pulling.contains(pl.getUniqueId())){ pl.setAllowFlight(false); pl.setFlying(false); } } private void pullPlayerTo(final Player player, final Location location) { final boolean isFlying = player.isFlying(); final boolean allowFlying = player.getAllowFlight(); if(!isFlying) { player.setFlying(true); player.setAllowFlight(true);} pulling.add(player.getUniqueId()); BukkitRunnable runnable = new DebugBukkitRunnable("GrapplingHookPull") { int i = 0; double dist = 100000; @Override public void runIntern() { //Have a safety abort: if(i++>100) {stopIt(); return;} //If gone offline -> set flying false! if(!player.isOnline() || player.isDead()) { stopIt(); return; } //Check if near enough: Location playerLocation = player.getLocation(); double curDist = playerLocation.distance(location); if(Math.abs(curDist - dist) < 0.4){ stopIt(); return; } dist = curDist; if(playerLocation.distance(location) < 1 || launchMap.values().contains(player.getName())){ stopIt(); return; } //Calculate the Direction: Vector vec = location.toVector().subtract(playerLocation.toVector()); vec.normalize().multiply(1.4); //Now check to be sure to not get into a solid block: Location teleportLocation = playerLocation.add(vec); Location up = teleportLocation.clone().add(0,2,0); if(up.getBlock().getType().isSolid()){ //We will never go into a solid block! stopIt(); return; } //Finally teleport. isGrapplingTeleport = true; player.teleport(teleportLocation); isGrapplingTeleport = false; player.setFlying(true); //every 4th tick -> do fancy particles. if(i % 2 == 0){ List<Location> between = getBetween(playerLocation, location, 10); for(Location loc : between){ VollotileCodeManager.getVollotileCode().sendParticleEffectToAll( ParticleEffects.CRIT, loc, new Vector(0,0,0), 0, 3); } } } private void stopIt(){ pulling.remove(player.getUniqueId()); cancel(); try{ if(!isFlying) player.setFlying(false); }catch(Throwable exp){} try{ if(!allowFlying) player.setAllowFlight(false); }catch(Throwable exp){} } private List<Location> getBetween(Location start, Location end, int amount){ start = start.clone(); end = end.clone(); Vector vec = end.toVector().subtract(start.toVector()); vec = vec.divide(new Vector(amount,amount,amount)); List<Location> locs = new LinkedList<Location>(); for(int i = 1; i < amount; i++){ start.add(vec); locs.add(start.clone()); } return locs; } }; runnable.runTaskTimer(plugin, 2, 2); } public static List<String> getHelpForTrait(){ List<String> helpList = new LinkedList<String>(); helpList.add(ChatColor.YELLOW + "You fire a grappling hook which lifts you."); return helpList; } @Override public boolean isBetterThan(Trait trait) { return false; } @TraitInfos(category="activate", traitName="GrapplingHookTrait", visible=true) @Override public void importTrait() { } @SuppressWarnings("deprecation") @Override public boolean canBeTriggered(EventWrapper wrapper) { Event event = wrapper.getEvent(); //TODO fix sometime... if(event instanceof PlayerInteractEvent){ PlayerInteractEvent Eevent = (PlayerInteractEvent) event; Player player = Eevent.getPlayer(); UUID id = player.getUniqueId(); if(player.getItemInHand().getType() == materialToUse && Eevent.getAction() == Action.RIGHT_CLICK_AIR && !launchMap.values().contains(id) && !pulling.contains(id)) { return true; } } if(event instanceof ProjectileHitEvent){ Projectile projectile = ((ProjectileHitEvent) event).getEntity(); if(launchMap.containsKey(projectile)){ return true; } } return false; } @Override public boolean isBindable() { return true; } @Override protected TraitResults bindCastIntern(RaCPlayer player) { EventWrapper event = EventWrapperFactory.buildFromEvent(new PlayerInteractEvent(player.getPlayer(), null, null, null, null)); return trigger(event); } }