/*******************************************************************************
* 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.ThrowItemTrait;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Set;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.entity.LivingEntity;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.inventory.InventoryPickupItemEvent;
import org.bukkit.event.player.PlayerPickupItemEvent;
import org.bukkit.event.world.ChunkUnloadEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.Plugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
import de.tobiyas.racesandclasses.RacesAndClasses;
import de.tobiyas.racesandclasses.playermanagement.player.RaCPlayer;
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.traitcontainer.traits.magic.AbstractMagicSpellTrait;
import de.tobiyas.racesandclasses.util.damage.PreEntityDamageEvent;
import de.tobiyas.racesandclasses.util.entitysearch.SearchEntity;
import de.tobiyas.racesandclasses.util.friend.EnemyChecker;
import de.tobiyas.racesandclasses.util.traitutil.TraitConfiguration;
import de.tobiyas.racesandclasses.util.traitutil.TraitConfigurationFailedException;
import de.tobiyas.racesandclasses.vollotile.ParticleContainer;
import de.tobiyas.racesandclasses.vollotile.Vollotile;
public class ThrowItemTrait extends AbstractMagicSpellTrait implements Listener {
/**
* The Damage to do.
*/
protected double damage = 1;
protected boolean addSmallOffset = false;
protected Material mat = Material.WOOD_SWORD;
protected double speed = 3;
protected int matDamageValue = 0;
protected ParticleContainer particles = null;
protected ParticleContainer particlesFromSelf = null;
protected final Set<Item> flying = new HashSet<Item>();
public ThrowItemTrait() {
Bukkit.getPluginManager().registerEvents(this, (Plugin)RacesAndClasses.getPlugin());
new BukkitRunnable(){
@Override
public void run() {
Iterator<Item> itemIt = flying.iterator();
while(itemIt.hasNext()){
Item item = itemIt.next();
if(!item.isValid()
|| item.getVelocity().length() <= 0.1) { //laying still.
item.remove();
itemIt.remove();
continue;
}
List<Entity> nearList = SearchEntity.inCircleAround(item, 2);
for(Entity near : nearList){
if(!(near instanceof LivingEntity)) continue;
if(!item.hasMetadata(DELETE_ON_DROP)) continue;
//check for near middle of character.
if(near.getLocation().add(0,1,0).distanceSquared(item.getLocation()) > 1) continue;
RaCPlayer thrower = (RaCPlayer) item.getMetadata(DELETE_ON_DROP).get(0).value();
if(thrower.getPlayer() == near) continue;
entityGotHitByItem(thrower, (LivingEntity) near);
item.remove();
itemIt.remove();
break;
}
}
}
}.runTaskTimer((Plugin)RacesAndClasses.getPlugin(), 1, 1);
}
@Override
public void deInit() {
super.deInit();
for(Item item : flying){
if(item.isValid()) item.remove();
}
}
@Override
public String getName() {
return "ThrowItemTrait";
}
@TraitConfigurationNeeded( fields = {
@TraitConfigurationField(fieldName = "damage", classToExpect = Double.class, optional = false),
@TraitConfigurationField(fieldName = "speed", classToExpect = Double.class, optional = true),
@TraitConfigurationField(fieldName = "material", classToExpect = Material.class, optional = true),
@TraitConfigurationField(fieldName = "materialDamageValue", classToExpect = Integer.class, optional = true),
@TraitConfigurationField(fieldName = "particle", classToExpect = String.class, optional = true),
@TraitConfigurationField(fieldName = "particleFromSelf", classToExpect = String.class, optional = true),
@TraitConfigurationField(fieldName = "addSmallOffset", classToExpect = Boolean.class, optional = true),
})
@Override
public void setConfiguration(TraitConfiguration configMap) throws TraitConfigurationFailedException {
super.setConfiguration(configMap);
if(configMap.containsKey("material")){
mat = configMap.getAsMaterial("material");
}
if(configMap.containsKey("addSmallOffset")){
addSmallOffset = configMap.getAsBool("addSmallOffset");
}
if(configMap.containsKey("materialDamageValue")){
matDamageValue = configMap.getAsInt("materialDamageValue");
}
if(configMap.containsKey("damage")){
damage = configMap.getAsDouble("damage");
}
if(configMap.containsKey("speed")){
speed = configMap.getAsDouble("speed");
}
if(configMap.containsKey("particle")){
particles = configMap.getAsParticleContainer("particle");
}
if(configMap.containsKey("particleFromSelf")){
particlesFromSelf = configMap.getAsParticleContainer("particleFromSelf");
}
}
public static List<String> getHelpForTrait(){
List<String> helpList = new LinkedList<String>();
helpList.add(ChatColor.YELLOW + "This Trait Throws an Item in the Direction you are looking at.");
return helpList;
}
@TraitInfos(category = "activate", traitName = "ThrowItemTrait", visible = true)
@Override
public void importTrait() {
}
@Override
public boolean isBetterThan(Trait trait) {
return false;
}
@TraitEventsUsed(registerdClasses = {})
@Override
public void generalInit() {
}
@Override
protected String getPrettyConfigIntern() {
return "Does damage on Stuff touching the Thrown Item.";
}
private final String DELETE_ON_DROP = "ITEM_DELETE_ON_DROP";
private final Random rand = new Random();
private void removeItemAfterX(final Item droppedItem, int time) {
new BukkitRunnable(){
@Override
public void run() {
if(droppedItem.isValid()){
droppedItem.remove();
}
}
}.runTaskLater((Plugin) RacesAndClasses.getPlugin(), 20 * time);
}
@EventHandler
public void chunkUnload(ChunkUnloadEvent event){
for(Entity entity : Arrays.asList(event.getChunk().getEntities())){
if(entity.hasMetadata(DELETE_ON_DROP)) entity.remove();
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void playerPickup(PlayerPickupItemEvent event){
Item item = event.getItem();
if(item.hasMetadata(DELETE_ON_DROP)){
RaCPlayer owner = (RaCPlayer) item.getMetadata(DELETE_ON_DROP).get(0).value();
if(owner == null) return;
event.setCancelled(true);
if(owner.getPlayer() == event.getPlayer()) return;
if(entityGotHitByItem(owner, event.getPlayer())){
item.remove();
flying.remove(item);
}
}
}
@EventHandler(priority = EventPriority.LOWEST)
public void hopperPickup(InventoryPickupItemEvent event){
Item item = event.getItem();
if(item.hasMetadata(DELETE_ON_DROP)){
event.setCancelled(true);
}
}
/**
* Indicates that a Hit has occured.
*
* @param thrower that threw
* @param target that was hit.
*
* @return true if item should be removed.
*/
private boolean entityGotHitByItem(RaCPlayer thrower, LivingEntity target){
if(thrower.getPlayer() == target) return false;
if(!EnemyChecker.areEnemies(thrower.getPlayer(), target)) return false;
double damage = PreEntityDamageEvent.getRealDamage(thrower.getPlayer(), target, DamageCause.CONTACT, modifyToPlayer(thrower, this.damage, "damage"));
if(damage <= 0) return false;
de.tobiyas.racesandclasses.util.bukkit.versioning.compatibility.CompatibilityModifier.LivingEntity
.safeDamageEntityByEntity(target, thrower.getPlayer(), damage);
if(particles != null) Vollotile.get().sendOwnParticleEffectToAll(particles, target.getEyeLocation());
return true;
}
@Override
protected void magicSpellTriggered(RaCPlayer player, TraitResults result) {
ItemStack item = new ItemStack(mat);
item.setDurability((byte)matDamageValue);
Item droppedItem = player.getLocation().getWorld().dropItem(player.getLocation().clone().add(0,1.5,0), item);
droppedItem.setItemStack(item);
Vector velocity = player.getPlayer().getLocation().getDirection().clone()
.multiply(speed);
if(addSmallOffset){
Vector offset = new Vector();
offset.setX(rand.nextDouble() * 0.1 - 0.05);
offset.setY(rand.nextDouble() * 0.1 - 0.05);
offset.setZ(rand.nextDouble() * 0.1 - 0.05);
velocity = velocity.add(offset);
}
//starts in direction of player sight.
droppedItem.setVelocity(velocity);
droppedItem.setMetadata(DELETE_ON_DROP, new FixedMetadataValue((Plugin) RacesAndClasses.getPlugin(), player));
//set pickup time to 1 to pick it up as soon as possible.
droppedItem.setPickupDelay(1);
removeItemAfterX(droppedItem, 5);
flying.add(droppedItem);
if(particlesFromSelf != null) Vollotile.get().sendOwnParticleEffectToAll(particlesFromSelf,
player.getLocation().clone().add(0,1,0));
result.copyFrom(TraitResults.True());
}
}