236 lines
9.6 KiB
Java
236 lines
9.6 KiB
Java
package electroblob.wizardry.spell;
|
|
|
|
import electroblob.wizardry.Wizardry;
|
|
import electroblob.wizardry.entity.living.EntityEvilWizard;
|
|
import electroblob.wizardry.item.SpellActions;
|
|
import electroblob.wizardry.registry.WizardryItems;
|
|
import electroblob.wizardry.registry.WizardryPotions;
|
|
import electroblob.wizardry.registry.WizardrySounds;
|
|
import electroblob.wizardry.util.AllyDesignationSystem;
|
|
import electroblob.wizardry.util.EntityUtils;
|
|
import electroblob.wizardry.util.ParticleBuilder;
|
|
import electroblob.wizardry.util.ParticleBuilder.Type;
|
|
import electroblob.wizardry.util.SpellModifiers;
|
|
import net.minecraft.entity.*;
|
|
import net.minecraft.entity.item.EntityArmorStand;
|
|
import net.minecraft.entity.passive.EntitySheep;
|
|
import net.minecraft.entity.player.EntityPlayer;
|
|
import net.minecraft.init.SoundEvents;
|
|
import net.minecraft.item.EnumDyeColor;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraft.potion.PotionEffect;
|
|
import net.minecraft.tileentity.TileEntityDispenser;
|
|
import net.minecraft.util.EnumFacing;
|
|
import net.minecraft.util.math.BlockPos;
|
|
import net.minecraft.util.math.Vec3d;
|
|
import net.minecraft.util.text.TextComponentTranslation;
|
|
import net.minecraft.world.World;
|
|
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
|
import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
|
|
import net.minecraftforge.event.entity.living.PotionEvent;
|
|
import net.minecraftforge.fml.common.Mod;
|
|
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
|
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
|
|
@Mod.EventBusSubscriber
|
|
public class MindControl extends SpellRay {
|
|
|
|
/** The NBT tag name for storing the controlling entity's UUID in the target's tag compound. */
|
|
public static final String NBT_KEY = "controllingEntity";
|
|
|
|
public MindControl(){
|
|
super("mind_control", SpellActions.POINT, false);
|
|
addProperties(EFFECT_DURATION);
|
|
}
|
|
|
|
@Override public boolean canBeCastBy(EntityLiving npc, boolean override) { return false; }
|
|
@Override public boolean canBeCastBy(TileEntityDispenser dispenser) { return false; }
|
|
|
|
@Override
|
|
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
|
|
|
if(EntityUtils.isLiving(target)){
|
|
|
|
if(!canControl(target)){
|
|
if(!world.isRemote){
|
|
if(caster instanceof EntityPlayer){
|
|
// Adds a message saying that the player/boss entity/wizard resisted mind control
|
|
((EntityPlayer)caster).sendStatusMessage(new TextComponentTranslation("spell.resist", target.getName(),
|
|
this.getNameForTranslationFormatted()), true);
|
|
}
|
|
}
|
|
|
|
}else if(target instanceof EntityLiving){
|
|
|
|
if(!world.isRemote){
|
|
if(!MindControl.findMindControlTarget((EntityLiving)target, caster, world)){
|
|
// If no valid target was found, this just acts like mind trick.
|
|
((EntityLiving)target).setAttackTarget(null);
|
|
}
|
|
}
|
|
|
|
if(target instanceof EntitySheep && ((EntitySheep)target).getFleeceColor() == EnumDyeColor.BLUE
|
|
&& EntityUtils.canDamageBlocks(caster, world)){
|
|
if(!world.isRemote) ((EntitySheep)target).setFleeceColor(EnumDyeColor.RED); // Wololo!
|
|
world.playSound(caster.posX, caster.posY, caster.posZ, SoundEvents.EVOCATION_ILLAGER_PREPARE_WOLOLO, WizardrySounds.SPELLS, 1, 1, false);
|
|
}
|
|
|
|
if(!world.isRemote) startControlling((EntityLiving)target, caster,
|
|
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
|
|
}
|
|
|
|
if(world.isRemote){
|
|
|
|
for(int i=0; i<10; i++){
|
|
ParticleBuilder.create(Type.DARK_MAGIC, world.rand, target.posX,
|
|
target.getEntityBoundingBox().minY + target.getEyeHeight(), target.posZ, 0.25, false)
|
|
.clr(0.8f, 0.2f, 1.0f).spawn(world);
|
|
ParticleBuilder.create(Type.DARK_MAGIC, world.rand, target.posX,
|
|
target.getEntityBoundingBox().minY + target.getEyeHeight(), target.posZ, 0.25, false)
|
|
.clr(0.2f, 0.04f, 0.25f).spawn(world);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
|
return false;
|
|
}
|
|
|
|
/** Returns true if the given entity can be mind controlled (i.e. is not a player, npc, evil wizard or boss). */
|
|
public static boolean canControl(Entity target){
|
|
return target instanceof EntityLiving && target.isNonBoss() && !(target instanceof INpc)
|
|
&& !(target instanceof EntityEvilWizard) && !Arrays.asList(Wizardry.settings.mindControlTargetsBlacklist)
|
|
.contains(EntityList.getKey(target.getClass()));
|
|
}
|
|
|
|
public static void startControlling(EntityLiving target, EntityLivingBase controller, int duration){
|
|
target.getEntityData().setUniqueId(NBT_KEY, controller.getUniqueID());
|
|
target.addPotionEffect(new PotionEffect(WizardryPotions.mind_control, duration, 0));
|
|
}
|
|
|
|
/**
|
|
* Finds the nearest creature to the given target which it is allowed to attack according to the given caster and
|
|
* sets it as the target's attack target. Handles both new and old AI and takes follow range into account. Defined
|
|
* here so it can be used both in the spell itself and in the potion effect (event handler).
|
|
*
|
|
* @param target The entity being mind controlled
|
|
* @param caster The entity doing the controlling
|
|
* @param world The world to look for targets in
|
|
* @return True if a new target was found and set, false if not.
|
|
*/
|
|
public static boolean findMindControlTarget(EntityLiving target, EntityLivingBase caster, World world){
|
|
|
|
// As of 1.1, this now uses the creature's follow range, like normal targeting. It also
|
|
// no longer lasts until the creature dies; instead it is a potion effect which continues to
|
|
// set the target until it wears off.
|
|
List<EntityLivingBase> possibleTargets = EntityUtils.getLivingWithinRadius(
|
|
target.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).getAttributeValue(),
|
|
target.posX, target.posY, target.posZ, world);
|
|
|
|
possibleTargets.remove(target);
|
|
possibleTargets.remove(target.getRidingEntity());
|
|
possibleTargets.removeIf(e -> e instanceof EntityArmorStand);
|
|
|
|
EntityLivingBase newAITarget = null;
|
|
|
|
for(EntityLivingBase possibleTarget : possibleTargets){
|
|
if(AllyDesignationSystem.isValidTarget(caster, possibleTarget) && (newAITarget == null
|
|
|| target.getDistance(possibleTarget) < target.getDistance(newAITarget))){
|
|
newAITarget = possibleTarget;
|
|
}
|
|
}
|
|
|
|
if(newAITarget != null){
|
|
target.setAttackTarget(newAITarget);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
/** Retrieves the given entity's controller and decides whether it needs to search for a target. */
|
|
private static void processTargeting(World world, EntityLiving entity, EntityLivingBase currentTarget){
|
|
|
|
if(entity.isPotionActive(WizardryPotions.mind_control) && MindControl.canControl(entity)){
|
|
|
|
NBTTagCompound entityNBT = entity.getEntityData();
|
|
|
|
if(entityNBT != null && entityNBT.hasUniqueId(MindControl.NBT_KEY)){
|
|
|
|
Entity caster = EntityUtils.getEntityByUUID(world, entityNBT.getUniqueId(MindControl.NBT_KEY));
|
|
|
|
if(caster instanceof EntityLivingBase){
|
|
|
|
// If the current target is already a valid mind control target, nothing happens.
|
|
if(AllyDesignationSystem.isValidTarget(caster, currentTarget)) return;
|
|
|
|
if(MindControl.findMindControlTarget(entity, (EntityLivingBase)caster, world)){
|
|
// If it worked, skip setting the target to null.
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// If the caster couldn't be found or no valid target was found, this just acts like mind trick.
|
|
entity.setAttackTarget(null);
|
|
}
|
|
}
|
|
|
|
@SubscribeEvent
|
|
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
|
// Tries to find a new target for mind-controlled creatures that do not currently have one
|
|
// When the mind-controlled creature does have a target, LivingSetAttackTargetEvent is used instead since it is
|
|
// more efficient (because it only fires when the entity tries to set a target)
|
|
// Of course, in survival this code is unlikely to be used much because the entity will always try to target the
|
|
// player and hence will rarely have no target.
|
|
if(event.getEntityLiving().isPotionActive(WizardryPotions.mind_control) && event.getEntityLiving() instanceof EntityLiving){
|
|
|
|
EntityLiving entity = (EntityLiving)event.getEntityLiving();
|
|
|
|
// Processes targeting if the current target is null or has died
|
|
if(((EntityLiving)event.getEntityLiving()).getAttackTarget() == null
|
|
|| !((EntityLiving)event.getEntityLiving()).getAttackTarget().isEntityAlive()){
|
|
|
|
processTargeting(entity.world, entity, null);
|
|
}
|
|
}
|
|
}
|
|
|
|
@SubscribeEvent
|
|
public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){
|
|
// The != null check prevents infinite loops with mind trick
|
|
if(event.getTarget() != null && event.getEntityLiving() instanceof EntityLiving)
|
|
processTargeting(event.getEntity().world, (EntityLiving)event.getEntityLiving(), event.getTarget());
|
|
}
|
|
|
|
@SubscribeEvent
|
|
public static void onPotionExpiryEvent(PotionEvent.PotionExpiryEvent event){
|
|
onEffectEnd(event.getPotionEffect(), event.getEntity());
|
|
}
|
|
|
|
@SubscribeEvent
|
|
public static void onPotionExpiryEvent(PotionEvent.PotionRemoveEvent event){
|
|
onEffectEnd(event.getPotionEffect(), event.getEntity());
|
|
}
|
|
|
|
private static void onEffectEnd(PotionEffect effect, Entity entity){
|
|
if(effect != null && effect.getPotion() == WizardryPotions.mind_control && entity instanceof EntityLiving){
|
|
((EntityLiving)entity).setAttackTarget(null); // End effect
|
|
((EntityLiving)entity).setRevengeTarget(null); // End effect
|
|
}
|
|
}
|
|
|
|
} |