/******************************************************************************* * Copyright 2014 Tobias Welther * * 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.entitystatusmanager.dot; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.bukkit.Location; import org.bukkit.entity.Entity; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntityDamageEvent; import org.bukkit.event.entity.EntityDeathEvent; import org.bukkit.event.entity.PlayerDeathEvent; import org.bukkit.scheduler.BukkitTask; import de.tobiyas.racesandclasses.RacesAndClasses; import de.tobiyas.racesandclasses.configuration.statusimun.StatusEffect; import de.tobiyas.racesandclasses.playermanagement.player.RaCPlayer; import de.tobiyas.racesandclasses.util.bukkit.versioning.compatibility.CompatibilityModifier; import de.tobiyas.racesandclasses.vollotile.ParticleContainer; import de.tobiyas.racesandclasses.vollotile.Vollotile; import de.tobiyas.util.schedule.DebugBukkitRunnable; public class DotManager implements Listener { /** * The plugin to call stuff on. */ private final RacesAndClasses plugin; /** * The taskID for the Bukkit task */ private BukkitTask bukkitTask; /** * The Poison Map for the Plugin */ private final Map<LivingEntity, Collection<DotContainer>> dotDamageMap = new ConcurrentHashMap<LivingEntity, Collection<DotContainer>>(); /** * Creates the PoisonManager */ public DotManager() { plugin = RacesAndClasses.getPlugin(); plugin.registerEvents(this); } /** * Inits the Manager */ public void init(){ dotDamageMap.clear(); if(bukkitTask == null){ bukkitTask = new DebugBukkitRunnable("PoisonTicker") { @Override public void runIntern() { Iterator<Map.Entry<LivingEntity, Collection<DotContainer>>> poisonTickerIterator = dotDamageMap.entrySet().iterator(); while(poisonTickerIterator.hasNext()){ Map.Entry<LivingEntity, Collection<DotContainer>> entry = poisonTickerIterator.next(); LivingEntity entity = entry.getKey(); Iterator<DotContainer> itDots = entry.getValue().iterator(); while(itDots.hasNext()){ DotContainer dotContainer = itDots.next(); if(entity.isDead() || !entity.isValid()){ poisonTickerIterator.remove(); break; } int newTicks = dotContainer.getTicks() - 1; dotContainer.setTicks(newTicks); if(newTicks % dotContainer.getDamageEveryTicks() == 0){ if(damageEntity(entity, dotContainer)) playDotParticleEffects(entity, dotContainer); } //Ticks is done -> End! if(newTicks < 0) itDots.remove(); } } } }.runTaskTimer(plugin, 5, 5); } } /** * Plays poison particle effects on the Entity. * @param entity to use the location on. */ protected void playDotParticleEffects(LivingEntity entity, DotContainer option) { Location loc = entity.getLocation().clone().add(0,1,0); ParticleContainer effect = option.getDamageType().getParticleContainer(); Vollotile.get().sendOwnParticleEffectToAll(effect, loc); } /** * Damages an Entity for the Options passed * * @param entity to damage * @param options to apply */ protected boolean damageEntity(LivingEntity entity, DotContainer options) { double damagePerTick = options.getDamageOnTick(); EntityDamageEvent event = CompatibilityModifier.EntityDamage.safeCreateEvent(entity, options.getDamageType().getCause(), damagePerTick); plugin.fireEventToBukkit(event); RaCPlayer damager = options.getDamager(); Player realDamager = damager.isOnline() ? damager.getRealPlayer() : null; if(!event.isCancelled()){ double newDamage = CompatibilityModifier.EntityDamage.safeGetDamage(event); CompatibilityModifier.LivingEntity.safeDamageEntityByEntity(entity, realDamager, newDamage); } return !event.isCancelled(); } /** * Removes all Stuff not needed any more. */ public void deinit(){ if(bukkitTask != null){ bukkitTask.cancel(); bukkitTask = null; } } /** * Dots an Entity. * * @param entity to Dot * @param builder the builder for the Dot. * * @return if it worked. */ public boolean dotEntity(LivingEntity entity, DotBuilder builder){ if(entity == null || !builder.valid()) return false; //Check imun: DamageType damageType = builder.getDamageType(); if(isImun(entity, damageType)) return false; //Build the container and check if we already have something with that name: DotContainer newContainer = builder.build(); Collection<DotContainer> dotsOfEntity = dotDamageMap.get(entity); //Add if not present! if(dotsOfEntity == null) { dotsOfEntity = new HashSet<>(); dotDamageMap.put(entity, dotsOfEntity); } //New is always better! :D for(Iterator<DotContainer> it = dotsOfEntity.iterator(); it.hasNext();){ DotContainer next = it.next(); if(next.getName().equalsIgnoreCase(newContainer.getName())) it.remove(); } //Finally add! dotsOfEntity.add(newContainer); return true; } /** * If the entity is imun against stun. * @param entity to check. * @param type to check * @return true if imun. */ private boolean isImun(Entity entity, DamageType type){ if(entity == null || type == null) return false; StatusEffect effect = null; switch(type){ case FIRE : case FIRE_TICK : case LAVA : effect = StatusEffect.FIRE; break; case POISON : effect = StatusEffect.POISON; break; default: effect = null; } String name = entity.getCustomName(); return plugin.getConfigManager().getStatusImunManager().isImun(name, effect); } /** * Removes all Poison effects from the entity. * * @param entity to un poison. * @return true if worked, false otherwise. */ public int removeAllDots(LivingEntity entity){ if(entity == null) return 0; Collection<DotContainer> removed = dotDamageMap.remove(entity); return removed == null ? 0 : removed.size(); } /** * Removes all dots with that name. * * @param entity to clear. * @param name to remove. * * @return true if worked, false otherwise. */ public int removeAllDots(LivingEntity entity, String name){ if(entity == null) return 0; Collection<DotContainer> containers = dotDamageMap.get(entity); if(containers == null) return 0; //Remove every dot with that name. int removed = 0; for(Iterator<DotContainer> it = containers.iterator(); it.hasNext(); ){ if(it.next().getName().equalsIgnoreCase(name)) { it.remove(); removed++; } } return removed; } /** * Removes all dots with that name. * * @param entity to clear. * @param name to remove. * * @return true if worked, false otherwise. */ public int removeAllDots(LivingEntity entity, DamageType type){ if(entity == null) return 0; Collection<DotContainer> containers = dotDamageMap.get(entity); if(containers == null) return 0; //Remove every dot with that name. int removed = 0; for(Iterator<DotContainer> it = containers.iterator(); it.hasNext(); ){ if(it.next().getDamageType() == type) { it.remove(); removed++; } } return removed; } /** * Gets all Dots of the entity. * <br>This passes a copy! * * @param entity to use. * @return all Dots. */ public Collection<DotContainer> getAllDotsOnEntity(LivingEntity entity){ if(entity == null) return new ArrayList<>(0); Collection<DotContainer> dots = dotDamageMap.get(entity); return dots == null ? new ArrayList<DotContainer>(0) : dots; } /** * Gets all Dots of the entity of a type. * <br>This passes a copy! * * @param entity to use. * @param type type to filter. * @return all Dots of the passed type. */ public Collection<DotContainer> getAllDotsOnEntity(LivingEntity entity, DamageType type) { if(entity == null || type == null) return new ArrayList<>(0); Collection<DotContainer> dots = getAllDotsOnEntity(entity); for(Iterator<DotContainer> it = dots.iterator(); it.hasNext();){ if(it.next().getDamageType() != type) it.remove(); } return dots; } //Simple functions to clean up: @EventHandler public void onEntityDied(EntityDeathEvent event){ dotDamageMap.remove(event.getEntity()); } @EventHandler public void onPlayerDied(PlayerDeathEvent event) { onEntityDied(event); } }