216 lines
9.4 KiB
Java
216 lines
9.4 KiB
Java
package electroblob.wizardry.spell;
|
|
|
|
import java.util.List;
|
|
|
|
import electroblob.wizardry.Wizardry;
|
|
import electroblob.wizardry.constants.Element;
|
|
import electroblob.wizardry.constants.SpellType;
|
|
import electroblob.wizardry.constants.Tier;
|
|
import electroblob.wizardry.entity.living.EntityEvilWizard;
|
|
import electroblob.wizardry.registry.WizardryItems;
|
|
import electroblob.wizardry.registry.WizardryPotions;
|
|
import electroblob.wizardry.registry.WizardrySounds;
|
|
import electroblob.wizardry.util.SpellModifiers;
|
|
import electroblob.wizardry.util.WizardryParticleType;
|
|
import electroblob.wizardry.util.WizardryUtilities;
|
|
import net.minecraft.entity.Entity;
|
|
import net.minecraft.entity.EntityLiving;
|
|
import net.minecraft.entity.EntityLivingBase;
|
|
import net.minecraft.entity.INpc;
|
|
import net.minecraft.entity.SharedMonsterAttributes;
|
|
import net.minecraft.entity.player.EntityPlayer;
|
|
import net.minecraft.item.EnumAction;
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
import net.minecraft.potion.PotionEffect;
|
|
import net.minecraft.util.EnumHand;
|
|
import net.minecraft.util.math.RayTraceResult;
|
|
import net.minecraft.util.text.TextComponentTranslation;
|
|
import net.minecraft.world.World;
|
|
import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
|
|
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
|
import net.minecraftforge.fml.common.Mod;
|
|
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
|
|
|
@Mod.EventBusSubscriber
|
|
public class MindControl extends Spell {
|
|
|
|
/** The NBT tag name for storing the controlling entity's UUID in the target's tag compound. Defined here in case
|
|
* it changes. */
|
|
public static final String NBT_KEY = "controllingEntity";
|
|
|
|
public MindControl() {
|
|
super(Tier.ADVANCED, 40, Element.NECROMANCY, "mind_control", SpellType.ATTACK, 150, EnumAction.NONE, false);
|
|
}
|
|
|
|
@Override
|
|
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) {
|
|
|
|
RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, caster, 8*modifiers.get(WizardryItems.range_upgrade));
|
|
|
|
if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){
|
|
|
|
EntityLivingBase target = (EntityLivingBase)rayTrace.entityHit;
|
|
|
|
if(!world.isRemote){
|
|
if(!canControl(target)){
|
|
// Adds a message saying that the player/boss entity/wizard resisted mind control
|
|
if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted()));
|
|
|
|
}else if(target instanceof EntityLiving){
|
|
|
|
if(!MindControl.findMindControlTarget((EntityLiving)target, caster, world)){
|
|
// If no valid target was found, this just acts like mind trick.
|
|
((EntityLiving)target).setAttackTarget(null);
|
|
}
|
|
|
|
NBTTagCompound entityNBT = target.getEntityData();
|
|
if(entityNBT != null) entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID());
|
|
|
|
((EntityLiving)target).addPotionEffect(new PotionEffect(WizardryPotions.mind_control, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0));
|
|
}
|
|
}else{
|
|
for(int i=0; i<10; i++){
|
|
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5,
|
|
target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5,
|
|
target.posZ - 0.25 + world.rand.nextDouble()*0.5,
|
|
0, 0, 0, 0, 0.8f, 0.2f, 1.0f);
|
|
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5,
|
|
target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5,
|
|
target.posZ - 0.25 + world.rand.nextDouble()*0.5,
|
|
0, 0, 0, 0, 0.2f, 0.04f, 0.25f);
|
|
}
|
|
}
|
|
target.playSound(WizardrySounds.SPELL_SUMMONING, 1.0f, 1.0f);
|
|
caster.swingArm(hand);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers) {
|
|
|
|
if(target != null){
|
|
if(!world.isRemote){
|
|
if(canControl(target)){
|
|
|
|
if(!MindControl.findMindControlTarget((EntityLiving)target, caster, world)){
|
|
// If no valid target was found, this just acts like mind trick.
|
|
((EntityLiving)target).setAttackTarget(null);
|
|
}
|
|
|
|
NBTTagCompound entityNBT = target.getEntityData();
|
|
if(entityNBT != null) entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID());
|
|
|
|
((EntityLiving)target).addPotionEffect(new PotionEffect(WizardryPotions.mind_control, (int)(600*modifiers.get(WizardryItems.duration_upgrade)), 0));
|
|
}
|
|
}else{
|
|
for(int i=0; i<10; i++){
|
|
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5,
|
|
target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5,
|
|
target.posZ - 0.25 + world.rand.nextDouble()*0.5,
|
|
0, 0, 0, 0, 0.8f, 0.2f, 1.0f);
|
|
Wizardry.proxy.spawnParticle(WizardryParticleType.DARK_MAGIC, world, target.posX - 0.25 + world.rand.nextDouble()*0.5,
|
|
target.getEntityBoundingBox().minY + target.getEyeHeight() - 0.25 + world.rand.nextDouble()*0.5,
|
|
target.posZ - 0.25 + world.rand.nextDouble()*0.5,
|
|
0, 0, 0, 0, 0.2f, 0.04f, 0.25f);
|
|
}
|
|
}
|
|
target.playSound(WizardrySounds.SPELL_SUMMONING, 1.0f, 1.0f);
|
|
caster.swingArm(hand);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public boolean canBeCastByNPCs(){
|
|
return true;
|
|
}
|
|
|
|
/** 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(EntityLivingBase target){
|
|
return target instanceof EntityLiving && target.isNonBoss() && !(target instanceof INpc)
|
|
&& !(target instanceof EntityEvilWizard);
|
|
}
|
|
|
|
/**
|
|
* 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 = WizardryUtilities.getEntitiesWithinRadius(
|
|
((EntityLiving)target).getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).getAttributeValue(),
|
|
target.posX, target.posY, target.posZ, world);
|
|
|
|
possibleTargets.remove(target);
|
|
|
|
EntityLivingBase newAITarget = null;
|
|
|
|
for(EntityLivingBase possibleTarget : possibleTargets){
|
|
if(WizardryUtilities.isValidTarget(caster, possibleTarget) && (newAITarget == null
|
|
|| target.getDistanceToEntity(possibleTarget) < target.getDistanceToEntity(newAITarget))){
|
|
newAITarget = possibleTarget;
|
|
}
|
|
}
|
|
|
|
if(newAITarget != null){
|
|
// From 1.7.10 - this seems not to work quite right; the entity appears to continue attacking this target
|
|
// after it gets killed (not noticeable in survival since it will target the player again immediately.)
|
|
((EntityLiving)target).setAttackTarget(newAITarget);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
@SubscribeEvent
|
|
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
|
// This was added because something got changed in the AI classes which means LivingSetAttackTargetEvent doesn't
|
|
// get fired when I want it to... so I'm firing it myself.
|
|
if(event.getEntityLiving().isPotionActive(WizardryPotions.mind_control) && event.getEntityLiving() instanceof EntityLiving
|
|
&& ((EntityLiving)event.getEntityLiving()).getAttackTarget() != null
|
|
&& !((EntityLiving)event.getEntityLiving()).getAttackTarget().isEntityAlive())
|
|
((EntityLiving)event.getEntityLiving()).setAttackTarget(null); // Causes the event to be fired
|
|
}
|
|
|
|
@SubscribeEvent
|
|
public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){
|
|
|
|
if(event.getEntityLiving().isPotionActive(WizardryPotions.mind_control) && MindControl.canControl(event.getEntityLiving())){
|
|
|
|
NBTTagCompound entityNBT = event.getEntityLiving().getEntityData();
|
|
|
|
if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY + "Most")){
|
|
|
|
Entity caster = WizardryUtilities.getEntityByUUID(event.getEntity().worldObj, entityNBT.getUniqueId(MindControl.NBT_KEY));
|
|
|
|
// If the target that the event tried to set is already a valid mind control target, nothing happens.
|
|
if(event.getTarget() != null && WizardryUtilities.isValidTarget(caster, event.getTarget())) return;
|
|
|
|
if(caster instanceof EntityLivingBase){
|
|
|
|
if(MindControl.findMindControlTarget((EntityLiving)event.getEntityLiving(), (EntityLivingBase)caster, event.getEntity().worldObj)){
|
|
// 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.
|
|
// If the target is null already, no need to set it to null, or infinite loops will occur.
|
|
if(event.getTarget() != null) ((EntityLiving)event.getEntityLiving()).setAttackTarget(null);
|
|
}
|
|
}
|
|
|
|
} |