That's one heck of a commit you've got there...
I may have got a bit behind with version control. A lot behind, in fact. Maybe I'll go back and split this sometime - then again, I probably won't. But hey, at least it's here!
This commit is contained in:
@@ -1,32 +1,32 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.EntityArc;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
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;
|
||||
|
||||
public class Arc extends SpellRay {
|
||||
|
||||
private static final float BASE_DAMAGE = 3;
|
||||
|
||||
public Arc(){
|
||||
super("arc", Tier.BASIC, Element.LIGHTNING, SpellType.ATTACK, 5, 15, false, 8, null);
|
||||
super("arc", false, EnumAction.NONE);
|
||||
this.aimAssist(0.6f);
|
||||
this.soundValues(1, 1.7f, 0.2f);
|
||||
this.addProperties(DAMAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
@@ -44,11 +44,9 @@ public class Arc extends SpellRay {
|
||||
target.getName(), this.getNameForTranslationFormatted()), true);
|
||||
}else{
|
||||
target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK),
|
||||
BASE_DAMAGE * modifiers.get(SpellModifiers.POTENCY));
|
||||
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
}
|
||||
|
||||
// TODO: Does this mean that players hit by the spell hear no sound?
|
||||
target.playSound(WizardrySounds.SPELL_SPARK, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -56,12 +54,12 @@ public class Arc extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
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, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,47 +1,43 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.living.EntityWizard;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.monster.EntitySpellcasterIllager;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventPriority;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class ArcaneJammer extends SpellRay {
|
||||
|
||||
public ArcaneJammer(){
|
||||
super("arcane_jammer", Tier.ADVANCED, Element.HEALING, SpellType.ATTACK, 30, 50, false, 10, WizardrySounds.SPELL_DEFLECTION);
|
||||
super("arcane_jammer", false, EnumAction.NONE);
|
||||
this.soundValues(0.7f, 1, 0.4f);
|
||||
this.addProperties(EFFECT_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
if(target instanceof EntityWizard && caster instanceof EntityPlayer)
|
||||
WizardryAdvancementTriggers.jam_wizard.triggerFor((EntityPlayer)caster);
|
||||
|
||||
if(!world.isRemote){
|
||||
((EntityLivingBase)target).addPotionEffect(new PotionEffect(WizardryPotions.arcane_jammer,
|
||||
(int)(300 * modifiers.get(WizardryItems.duration_upgrade)), 0));
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,12 +45,12 @@ public class ArcaneJammer extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
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, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -64,10 +60,18 @@ public class ArcaneJammer extends SpellRay {
|
||||
.spawn(world);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@SubscribeEvent(priority = EventPriority.HIGHEST) // Prevents all spells so it comes before everything else
|
||||
public static void onSpellCastPreEvent(SpellCastEvent.Pre event){
|
||||
// Arcane jammer prevents spell casting.
|
||||
if(event.getCaster() != null && event.getCaster().isPotionActive(WizardryPotions.arcane_jammer)) event.setCanceled(true);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingEvent.LivingUpdateEvent event){
|
||||
if(event.getEntity() instanceof EntitySpellcasterIllager
|
||||
&& event.getEntityLiving().isPotionActive(WizardryPotions.arcane_jammer)){
|
||||
((EntitySpellcasterIllager)event.getEntity()).setSpellType(EntitySpellcasterIllager.SpellType.NONE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.util.AllyDesignationSystem;
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.event.world.ExplosionEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class ArcaneLock extends SpellRay {
|
||||
|
||||
/** The NBT tag name for storing the owner's UUID in the tile entity data. */
|
||||
public static final String NBT_KEY = "arcaneLockOwner";
|
||||
|
||||
public ArcaneLock(){
|
||||
super("arcane_lock", false, EnumAction.NONE);
|
||||
}
|
||||
|
||||
@Override public boolean requiresPacket(){ return true; }
|
||||
|
||||
@Override public boolean canBeCastByDispensers(){ return false; }
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return false; }
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(caster instanceof EntityPlayer){
|
||||
|
||||
TileEntity tileentity = world.getTileEntity(pos);
|
||||
|
||||
if(tileentity != null){
|
||||
|
||||
if(tileentity.getTileData().hasUniqueId(NBT_KEY)){
|
||||
// Unlocking
|
||||
if(world.getPlayerEntityByUUID(tileentity.getTileData().getUniqueId(NBT_KEY)) == caster){
|
||||
NBTExtras.removeUniqueId(tileentity.getTileData(), NBT_KEY);
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
// Locking
|
||||
tileentity.getTileData().setUniqueId(NBT_KEY, caster.getUniqueID());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLeftClickBlockEvent(PlayerInteractEvent.LeftClickBlock event){
|
||||
|
||||
if(!canBypassLocks(event.getEntityPlayer())){
|
||||
|
||||
TileEntity tileentity = event.getWorld().getTileEntity(event.getPos());
|
||||
|
||||
// Prevents arcane-locked containers from being broken
|
||||
// Need to check if it has the unique id first because if it is absent getUniqueId will return the nil UUID
|
||||
if(tileentity != null && tileentity.getTileData().hasUniqueId(ArcaneLock.NBT_KEY)){
|
||||
// Only the player that owns the lock may break the container
|
||||
// If nobody owns it (i.e. it's part of a shrine), player will be null
|
||||
// Why is getUniqueId marked @Nullable? It literally creates a UUID and returns it!
|
||||
if(event.getEntityPlayer().getUniqueID() != tileentity.getTileData().getUniqueId(ArcaneLock.NBT_KEY)){
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onRightClickBlockEvent(PlayerInteractEvent.RightClickBlock event){
|
||||
|
||||
if(!canBypassLocks(event.getEntityPlayer())){
|
||||
|
||||
TileEntity tileentity = event.getWorld().getTileEntity(event.getPos());
|
||||
|
||||
// Prevents arcane-locked containers from being opened
|
||||
// Need to check if it has the unique id first because if it is absent getUniqueId will return the nil UUID
|
||||
if(tileentity != null && tileentity.getTileData().hasUniqueId(ArcaneLock.NBT_KEY)){
|
||||
// Why is getUniqueId marked @Nullable? It literally creates a UUID and returns it!
|
||||
EntityPlayer owner = event.getWorld().getPlayerEntityByUUID(tileentity.getTileData().getUniqueId(ArcaneLock.NBT_KEY));
|
||||
// Only the player that owns the lock or an ally of that player may open the container
|
||||
// If nobody owns it (i.e. it's part of a shrine, or the owner logged out), player will be null
|
||||
// Unfortunately we can't get the owner's allies if the owner is offline
|
||||
// Perhaps if this crops up again we can store inverted 'ally of' maps as well?
|
||||
if(owner == null || (owner != event.getEntityPlayer() && !AllyDesignationSystem.isPlayerAlly(owner, event.getEntityPlayer()))){
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean canBypassLocks(EntityPlayer player){
|
||||
|
||||
if(!player.isCreative()) return false;
|
||||
if(Wizardry.settings.creativeBypassesArcaneLock) return true;
|
||||
MinecraftServer server = player.world.getMinecraftServer();
|
||||
return server != null && WizardryUtilities.isPlayerOp(player, server);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onExplosionEvent(ExplosionEvent.Detonate event){
|
||||
// Prevents arcane-locked containers from being exploded
|
||||
event.getAffectedBlocks().removeIf(pos -> event.getWorld().getTileEntity(pos) != null
|
||||
&& event.getWorld().getTileEntity(pos).getTileData().hasUniqueId(NBT_KEY));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,23 +1,20 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.construct.EntityArrowRain;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class ArrowRain extends SpellConstructRanged<EntityArrowRain> {
|
||||
|
||||
public ArrowRain(){
|
||||
super("arrow_rain", Tier.MASTER, Element.SORCERY, SpellType.ATTACK, 75, 300, EntityArrowRain::new, 120, 20, WizardrySounds.SPELL_SUMMONING);
|
||||
super("arrow_rain", EntityArrowRain::new, false);
|
||||
this.floor(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean spawnConstruct(World world, double x, double y, double z, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
protected boolean spawnConstruct(World world, double x, double y, double z, EnumFacing side, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
|
||||
// Moves the entity back towards the caster a bit, so the area of effect is better centred on the position.
|
||||
// 3 is the distance to move the entity back towards the caster.
|
||||
@@ -29,11 +26,11 @@ public class ArrowRain extends SpellConstructRanged<EntityArrowRain> {
|
||||
// Moves the entity up 5 blocks so that it is above mobs' heads.
|
||||
y += 5;
|
||||
|
||||
return super.spawnConstruct(world, x, y, z, caster, modifiers);
|
||||
return super.spawnConstruct(world, x, y, z, side, caster, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConstructExtras(EntityArrowRain construct, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
protected void addConstructExtras(EntityArrowRain construct, EnumFacing side, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
// Makes the arrows shoot in the direction the caster was looking when they cast the spell.
|
||||
construct.rotationYaw = caster.rotationYawHead;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
@@ -10,79 +7,48 @@ import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Banish extends SpellRay {
|
||||
|
||||
public static final String MINIMUM_TELEPORT_DISTANCE = "minimum_teleport_distance";
|
||||
public static final String MAXIMUM_TELEPORT_DISTANCE = "maximum_teleport_distance";
|
||||
|
||||
public Banish(){
|
||||
super("banish", Tier.APPRENTICE, Element.NECROMANCY, SpellType.ATTACK, 15, 40, false, 10, SoundEvents.ENTITY_ENDERMEN_TELEPORT);
|
||||
super("banish", false, EnumAction.NONE);
|
||||
this.addProperties(MINIMUM_TELEPORT_DISTANCE, MAXIMUM_TELEPORT_DISTANCE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(target instanceof EntityLivingBase){
|
||||
|
||||
EntityLivingBase entity = (EntityLivingBase)target;
|
||||
|
||||
double radius = (8 + world.rand.nextDouble() * 8) * modifiers.get(WizardryItems.range_upgrade);
|
||||
double angle = world.rand.nextDouble() * Math.PI * 2;
|
||||
double minRadius = getProperty(MINIMUM_TELEPORT_DISTANCE).doubleValue();
|
||||
double maxRadius = getProperty(MAXIMUM_TELEPORT_DISTANCE).doubleValue();
|
||||
double radius = (minRadius + world.rand.nextDouble() * maxRadius-minRadius) * modifiers.get(WizardryItems.blast_upgrade);
|
||||
|
||||
int x = MathHelper.floor(entity.posX + Math.sin(angle) * radius);
|
||||
int z = MathHelper.floor(entity.posZ - Math.cos(angle) * radius);
|
||||
int y = WizardryUtilities.getNearestFloorLevel(world,
|
||||
new BlockPos(x, (int)caster.getEntityBoundingBox().minY, z), (int)radius);
|
||||
|
||||
if(world.isRemote){
|
||||
for(int i=0; i<10; i++){
|
||||
double dx1 = entity.posX;
|
||||
double dy1 = entity.getEntityBoundingBox().minY + entity.height * world.rand.nextFloat();
|
||||
double dz1 = entity.posZ;
|
||||
world.spawnParticle(EnumParticleTypes.PORTAL, dx1, dy1, dz1, world.rand.nextDouble() - 0.5,
|
||||
world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5);
|
||||
}
|
||||
|
||||
// Can't be bothered to route this through the proxies!
|
||||
if(entity == net.minecraft.client.Minecraft.getMinecraft().player)
|
||||
electroblob.wizardry.client.WizardryClientEventHandler.playBlinkEffect();
|
||||
}
|
||||
|
||||
if(y > -1){
|
||||
|
||||
// This means stuff like snow layers is ignored, meaning when on snow-covered ground the target does
|
||||
// not teleport 1 block above the ground.
|
||||
if(!world.getBlockState(new BlockPos(x, y, z)).getMaterial().blocksMovement()){
|
||||
y--;
|
||||
}
|
||||
|
||||
if(world.getBlockState(new BlockPos(x, y + 1, z)).getMaterial().blocksMovement()
|
||||
|| world.getBlockState(new BlockPos(x, y + 2, z)).getMaterial().blocksMovement()){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!world.isRemote){
|
||||
entity.setPositionAndUpdate(x + 0.5, y + 1, z + 0.5);
|
||||
}
|
||||
|
||||
entity.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f);
|
||||
}
|
||||
teleport(entity, world, radius);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
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, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -92,4 +58,52 @@ public class Banish extends SpellRay {
|
||||
ParticleBuilder.create(Type.DARK_MAGIC).pos(x, y, z).clr(0.2f, 0, 0.2f).spawn(world);
|
||||
}
|
||||
|
||||
// Extracted as a separate method for external use
|
||||
public boolean teleport(EntityLivingBase entity, World world, double radius){
|
||||
|
||||
float angle = world.rand.nextFloat() * (float)Math.PI * 2;
|
||||
|
||||
int x = MathHelper.floor(entity.posX + MathHelper.sin(angle) * radius);
|
||||
int z = MathHelper.floor(entity.posZ - MathHelper.cos(angle) * radius);
|
||||
Integer y = WizardryUtilities.getNearestFloor(world,
|
||||
new BlockPos(x, (int)entity.getEntityBoundingBox().minY, z), (int)radius);
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
for(int i=0; i<10; i++){
|
||||
double dx1 = entity.posX;
|
||||
double dy1 = entity.getEntityBoundingBox().minY + entity.height * world.rand.nextFloat();
|
||||
double dz1 = entity.posZ;
|
||||
world.spawnParticle(EnumParticleTypes.PORTAL, dx1, dy1, dz1, world.rand.nextDouble() - 0.5,
|
||||
world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5);
|
||||
}
|
||||
|
||||
// Can't be bothered to route this through the proxies!
|
||||
if(entity == net.minecraft.client.Minecraft.getMinecraft().player)
|
||||
electroblob.wizardry.client.WizardryClientEventHandler.playBlinkEffect();
|
||||
}
|
||||
|
||||
if(y != null){
|
||||
|
||||
// This means stuff like snow layers is ignored, meaning when on snow-covered ground the target does
|
||||
// not teleport 1 block above the ground.
|
||||
if(!world.getBlockState(new BlockPos(x, y, z)).getMaterial().blocksMovement()){
|
||||
y--;
|
||||
}
|
||||
|
||||
if(world.getBlockState(new BlockPos(x, y + 1, z)).getMaterial().blocksMovement()
|
||||
|| world.getBlockState(new BlockPos(x, y + 2, z)).getMaterial().blocksMovement()){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!world.isRemote){
|
||||
entity.setPositionAndUpdate(x + 0.5, y + 1, z + 0.5);
|
||||
}
|
||||
|
||||
this.playSound(world, entity, 0, -1, new SpellModifiers());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.RayTracer;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
@@ -22,14 +19,15 @@ import net.minecraft.world.World;
|
||||
public class Blink extends Spell {
|
||||
|
||||
public Blink(){
|
||||
super("blink", Tier.APPRENTICE, Element.SORCERY, SpellType.UTILITY, 15, 25, EnumAction.NONE, false);
|
||||
super("blink", EnumAction.NONE, false);
|
||||
addProperties(RANGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
RayTraceResult rayTrace = WizardryUtilities.standardBlockRayTrace(world, caster,
|
||||
25 * modifiers.get(WizardryItems.range_upgrade), false);
|
||||
RayTraceResult rayTrace = RayTracer.standardBlockRayTrace(world, caster,
|
||||
getProperty(RANGE).doubleValue() * modifiers.get(WizardryItems.range_upgrade), false);
|
||||
|
||||
// It's worth noting that on the client side, the cast() method only gets called if the server side
|
||||
// cast method succeeded, so you need not check any conditions for spawning particles.
|
||||
@@ -52,8 +50,8 @@ public class Blink extends Spell {
|
||||
|
||||
BlockPos pos = rayTrace.getBlockPos();
|
||||
|
||||
// Can't teleport onto the ceiling.
|
||||
if(rayTrace.sideHit == EnumFacing.DOWN) return false;
|
||||
// Leave space for the player's head
|
||||
if(rayTrace.sideHit == EnumFacing.DOWN) pos = pos.down();
|
||||
|
||||
// This means stuff like snow layers is ignored, meaning when on snow-covered ground the player does
|
||||
// not teleport 1 block above the ground.
|
||||
@@ -61,7 +59,7 @@ public class Blink extends Spell {
|
||||
pos = pos.down();
|
||||
}
|
||||
|
||||
pos = rayTrace.getBlockPos().offset(rayTrace.sideHit);
|
||||
pos = pos.offset(rayTrace.sideHit);
|
||||
|
||||
// Prevents the player from teleporting into blocks and suffocating.
|
||||
if(world.getBlockState(pos).getMaterial().blocksMovement()
|
||||
@@ -70,11 +68,11 @@ public class Blink extends Spell {
|
||||
}
|
||||
|
||||
// Plays before and after so it is heard from both positions
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
if(!world.isRemote) caster.setPositionAndUpdate(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5);
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
caster.swingArm(hand);
|
||||
return true;
|
||||
}
|
||||
@@ -86,14 +84,14 @@ public class Blink extends Spell {
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target,
|
||||
SpellModifiers modifiers){
|
||||
|
||||
double angle = Math.atan2(target.posZ - caster.posZ, target.posX - caster.posX)
|
||||
+ world.rand.nextDouble() * Math.PI;
|
||||
float angle = (float)(Math.atan2(target.posZ - caster.posZ, target.posX - caster.posX)
|
||||
+ world.rand.nextDouble() * Math.PI);
|
||||
double radius = caster.getDistance(target.posX, target.getEntityBoundingBox().minY, target.posZ)
|
||||
+ world.rand.nextDouble() * 3.0d;
|
||||
|
||||
int x = MathHelper.floor(target.posX + Math.sin(angle) * radius);
|
||||
int z = MathHelper.floor(target.posZ - Math.cos(angle) * radius);
|
||||
int y = WizardryUtilities.getNearestFloorLevel(world, new BlockPos(caster), (int)radius);
|
||||
int x = MathHelper.floor(target.posX + MathHelper.sin(angle) * radius);
|
||||
int z = MathHelper.floor(target.posZ - MathHelper.cos(angle) * radius);
|
||||
Integer y = WizardryUtilities.getNearestFloor(world, new BlockPos(caster), (int)radius);
|
||||
|
||||
// It's worth noting that on the client side, the cast() method only gets called if the server side
|
||||
// cast method succeeded, so you need not check any conditions for spawning particles.
|
||||
@@ -109,7 +107,7 @@ public class Blink extends Spell {
|
||||
}
|
||||
}
|
||||
|
||||
if(y > -1){
|
||||
if(y != null){
|
||||
|
||||
// This means stuff like snow layers is ignored, meaning when on snow-covered ground the caster does
|
||||
// not teleport 1 block above the ground.
|
||||
@@ -123,13 +121,13 @@ public class Blink extends Spell {
|
||||
}
|
||||
|
||||
// Plays before and after so it is heard from both positions
|
||||
caster.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
if(!world.isRemote){
|
||||
caster.setPositionAndUpdate(x + 0.5, y + 1, z + 0.5);
|
||||
}
|
||||
|
||||
caster.playSound(SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
caster.swingArm(hand);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.construct.EntityBubble;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -13,40 +9,30 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Bubble extends SpellRay {
|
||||
|
||||
public Bubble(){
|
||||
super("bubble", Tier.APPRENTICE, Element.EARTH, SpellType.ATTACK, 15, 20, false, 10, WizardrySounds.SPELL_ICE);
|
||||
super("bubble", false, EnumAction.NONE);
|
||||
this.soundValues(0.5f, 1.1f, 0.2f);
|
||||
addProperties(DURATION);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
// This spell uses more than one sound, so this is required...
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, modifiers);
|
||||
if(flag) WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_GENERIC_SWIM, 1, 1 + 0.2f * world.rand.nextFloat());
|
||||
return flag;
|
||||
protected SoundEvent[] createSounds(){
|
||||
return this.createSoundsWithSuffixes("shoot", "splash");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, target, modifiers);
|
||||
if(flag) caster.playSound(SoundEvents.ENTITY_GENERIC_SWIM, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F);
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
@@ -57,7 +43,7 @@ public class Bubble extends SpellRay {
|
||||
EntityBubble bubble = new EntityBubble(world);
|
||||
bubble.setPosition(target.posX, target.posY, target.posZ);
|
||||
bubble.setCaster(caster);
|
||||
bubble.lifetime = ((int)(200 * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
bubble.lifetime = ((int)(getProperty(DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
bubble.isDarkOrb = false;
|
||||
bubble.damageMultiplier = modifiers.get(SpellModifiers.POTENCY);
|
||||
|
||||
@@ -70,12 +56,12 @@ public class Bubble extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
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, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,79 +1,81 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.EntityArc;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.*;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
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 java.util.List;
|
||||
|
||||
public class ChainLightning extends SpellRay {
|
||||
|
||||
private static final float PRIMARY_DAMAGE = 10;
|
||||
private static final float SECONDARY_DAMAGE = 8;
|
||||
private static final float TERTIARY_DAMAGE = 6;
|
||||
public static final String PRIMARY_DAMAGE = "primary_damage";
|
||||
public static final String SECONDARY_DAMAGE = "secondary_damage";
|
||||
public static final String TERTIARY_DAMAGE = "tertiary_damage";
|
||||
|
||||
private static final double PRIMARY_RANGE = 10;
|
||||
private static final double SECONDARY_RANGE = 5;
|
||||
private static final double TERTIARY_RANGE = 5;
|
||||
public static final String SECONDARY_RANGE = "secondary_range";
|
||||
public static final String TERTIARY_RANGE = "tertiary_range";
|
||||
|
||||
private static final int SECONDARY_MAX_TARGETS = 5;
|
||||
private static final int TERTIARY_MAX_TARGETS = 2; // This is per secondary target, giving 10 in total
|
||||
public static final String SECONDARY_MAX_TARGETS = "secondary_max_targets";
|
||||
public static final String TERTIARY_MAX_TARGETS = "tertiary_max_targets"; // This is per secondary target
|
||||
|
||||
public ChainLightning(){
|
||||
super("chain_lightning", Tier.ADVANCED, Element.LIGHTNING, SpellType.ATTACK, 25, 50, false, PRIMARY_RANGE, null);
|
||||
super("chain_lightning", false, EnumAction.NONE);
|
||||
this.aimAssist(0.6f);
|
||||
this.soundValues(1, 1.7f, 0.2f);
|
||||
addProperties(PRIMARY_DAMAGE, SECONDARY_DAMAGE, TERTIARY_DAMAGE, SECONDARY_RANGE, TERTIARY_RANGE,
|
||||
SECONDARY_MAX_TARGETS, TERTIARY_MAX_TARGETS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
// Anything can be attacked with the initial arc, because the player has control over where it goes. If they
|
||||
// hit a minion or an ally, it's their problem!
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
electrocute(world, caster, caster, target, PRIMARY_DAMAGE * modifiers.get(SpellModifiers.POTENCY));
|
||||
electrocute(world, caster, origin, target, getProperty(PRIMARY_DAMAGE).floatValue()
|
||||
* modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
// Secondary chaining effect
|
||||
List<EntityLivingBase> secondaryTargets = WizardryUtilities.getEntitiesWithinRadius(SECONDARY_RANGE,
|
||||
target.posX, target.posY + target.height / 2, target.posZ, world);
|
||||
List<EntityLivingBase> secondaryTargets = WizardryUtilities.getEntitiesWithinRadius(
|
||||
getProperty(SECONDARY_RANGE).doubleValue(), target.posX, target.posY + target.height / 2, target.posZ, world);
|
||||
|
||||
secondaryTargets.remove(target);
|
||||
secondaryTargets.removeIf(e -> !WizardryUtilities.isLiving(e));
|
||||
secondaryTargets.removeIf(e -> !WizardryUtilities.isValidTarget(caster, e));
|
||||
if(secondaryTargets.size() > SECONDARY_MAX_TARGETS) secondaryTargets = secondaryTargets.subList(0, SECONDARY_MAX_TARGETS);
|
||||
secondaryTargets.removeIf(e -> !AllyDesignationSystem.isValidTarget(caster, e));
|
||||
if(secondaryTargets.size() > getProperty(SECONDARY_MAX_TARGETS).intValue())
|
||||
secondaryTargets = secondaryTargets.subList(0, getProperty(SECONDARY_MAX_TARGETS).intValue());
|
||||
|
||||
for(EntityLivingBase secondaryTarget : secondaryTargets){
|
||||
|
||||
electrocute(world, caster, target, secondaryTarget,
|
||||
SECONDARY_DAMAGE * modifiers.get(SpellModifiers.POTENCY));
|
||||
electrocute(world, caster, target.getPositionVector().add(0, target.height/2, 0), secondaryTarget,
|
||||
getProperty(SECONDARY_DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
// Tertiary chaining effect
|
||||
|
||||
List<EntityLivingBase> tertiaryTargets = WizardryUtilities.getEntitiesWithinRadius(TERTIARY_RANGE,
|
||||
secondaryTarget.posX, secondaryTarget.posY + secondaryTarget.height / 2,
|
||||
secondaryTarget.posZ, world);
|
||||
List<EntityLivingBase> tertiaryTargets = WizardryUtilities.getEntitiesWithinRadius(
|
||||
getProperty(TERTIARY_RANGE).doubleValue(), secondaryTarget.posX,
|
||||
secondaryTarget.posY + secondaryTarget.height / 2, secondaryTarget.posZ, world);
|
||||
|
||||
tertiaryTargets.remove(target);
|
||||
tertiaryTargets.removeAll(secondaryTargets);
|
||||
tertiaryTargets.removeIf(e -> !WizardryUtilities.isLiving(e));
|
||||
tertiaryTargets.removeIf(e -> !WizardryUtilities.isValidTarget(caster, e));
|
||||
if(tertiaryTargets.size() > TERTIARY_MAX_TARGETS) tertiaryTargets = tertiaryTargets.subList(0, TERTIARY_MAX_TARGETS);
|
||||
tertiaryTargets.removeIf(e -> !AllyDesignationSystem.isValidTarget(caster, e));
|
||||
if(tertiaryTargets.size() > getProperty(TERTIARY_MAX_TARGETS).intValue())
|
||||
tertiaryTargets = tertiaryTargets.subList(0, getProperty(TERTIARY_MAX_TARGETS).intValue());
|
||||
|
||||
for(EntityLivingBase tertiaryTarget : tertiaryTargets){
|
||||
electrocute(world, caster, secondaryTarget, tertiaryTarget,
|
||||
TERTIARY_DAMAGE * modifiers.get(SpellModifiers.POTENCY));
|
||||
electrocute(world, caster, secondaryTarget.getPositionVector().add(0, secondaryTarget.height/2, 0),
|
||||
tertiaryTarget, getProperty(TERTIARY_DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,16 +86,16 @@ public class ChainLightning extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
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, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
private void electrocute(World world, Entity caster, Entity origin, Entity target, float damage){
|
||||
private void electrocute(World world, Entity caster, Vec3d origin, Entity target, float damage){
|
||||
|
||||
if(MagicDamage.isEntityImmune(DamageType.SHOCK, target)){
|
||||
if(!world.isRemote && caster instanceof EntityPlayer) ((EntityPlayer)caster).sendStatusMessage(
|
||||
@@ -111,7 +113,7 @@ public class ChainLightning extends SpellRay {
|
||||
ParticleBuilder.spawnShockParticles(world, target.posX, target.getEntityBoundingBox().minY + target.height/2, target.posZ);
|
||||
}
|
||||
|
||||
target.playSound(WizardrySounds.SPELL_SPARK, 1, 1.5f + 0.4f * world.rand.nextFloat());
|
||||
//target.playSound(WizardrySounds.SPELL_SPARK, 1, 1.5f + 0.4f * world.rand.nextFloat());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.data.IVariable;
|
||||
import electroblob.wizardry.data.Persistence;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventPriority;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class Charge extends Spell {
|
||||
|
||||
public static final IVariable<Integer> CHARGE_TIME = new IVariable.Variable<Integer>(Persistence.NEVER).withTicker(Charge::update);
|
||||
public static final IVariable<SpellModifiers> CHARGE_MODIFIERS = new IVariable.Variable<>(Persistence.NEVER);
|
||||
|
||||
public static final String CHARGE_SPEED = "charge_speed";
|
||||
public static final String KNOCKBACK_STRENGTH = "knockback_strength";
|
||||
|
||||
private static final double EXTRA_HIT_MARGIN = 1;
|
||||
|
||||
public Charge(){
|
||||
super("charge", EnumAction.NONE, false);
|
||||
addProperties(CHARGE_SPEED, DURATION, DAMAGE, KNOCKBACK_STRENGTH);
|
||||
this.soundValues(0.6f, 1, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
WizardData.get(caster).setVariable(CHARGE_TIME, (int)(getProperty(DURATION).floatValue()
|
||||
* modifiers.get(WizardryItems.duration_upgrade)));
|
||||
|
||||
WizardData.get(caster).setVariable(CHARGE_MODIFIERS, modifiers);
|
||||
|
||||
if(world.isRemote) world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, caster.posX, caster.posY + caster.height/2, caster.posZ, 0, 0, 0);
|
||||
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int update(EntityPlayer player, Integer chargeTime){
|
||||
|
||||
if(chargeTime == null) chargeTime = 0;
|
||||
|
||||
if(chargeTime > 0){
|
||||
|
||||
SpellModifiers modifiers = WizardData.get(player).getVariable(CHARGE_MODIFIERS);
|
||||
if(modifiers == null) modifiers = new SpellModifiers();
|
||||
|
||||
Vec3d look = player.getLookVec();
|
||||
|
||||
float speed = Spells.charge.getProperty(Charge.CHARGE_SPEED).floatValue() * modifiers.get(WizardryItems.range_upgrade);
|
||||
|
||||
player.motionX = look.x * speed;
|
||||
player.motionZ = look.z * speed;
|
||||
|
||||
if(player.world.isRemote){
|
||||
for(int i = 0; i < 5; i++){
|
||||
ParticleBuilder.create(Type.SPARK, player).spawn(player.world);
|
||||
}
|
||||
}
|
||||
|
||||
List<EntityLivingBase> collided = player.world.getEntitiesWithinAABB(EntityLivingBase.class, player.getEntityBoundingBox().grow(EXTRA_HIT_MARGIN));
|
||||
|
||||
collided.remove(player);
|
||||
|
||||
float damage = Spells.charge.getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY);
|
||||
float knockback = Spells.charge.getProperty(KNOCKBACK_STRENGTH).floatValue();
|
||||
|
||||
collided.forEach(e -> e.attackEntityFrom(MagicDamage.causeDirectMagicDamage(player, MagicDamage.DamageType.SHOCK), damage));
|
||||
collided.forEach(e -> e.addVelocity(player.motionX * knockback, player.motionY * knockback + 0.3f, player.motionZ * knockback));
|
||||
|
||||
if(player.world.isRemote) player.world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE,
|
||||
player.posX + player.motionX, player.posY + player.height/2, player.posZ + player.motionZ, 0, 0, 0);
|
||||
|
||||
if(collided.isEmpty()) chargeTime--;
|
||||
else{
|
||||
WizardryUtilities.playSoundAtPlayer(player, SoundEvents.ENTITY_GENERIC_HURT, 1, 1);
|
||||
chargeTime = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return chargeTime;
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGH)
|
||||
public static void onLivingAttackEvent(LivingAttackEvent event){
|
||||
// Players are immune to melee damage while charging
|
||||
if(event.getEntity() instanceof EntityPlayer && event.getSource().getTrueSource() instanceof EntityLivingBase){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getEntity();
|
||||
EntityLivingBase attacker = (EntityLivingBase)event.getSource().getTrueSource();
|
||||
|
||||
if(WizardData.get(player) != null){
|
||||
|
||||
Integer chargeTime = WizardData.get(player).getVariable(CHARGE_TIME);
|
||||
|
||||
if(chargeTime != null && chargeTime > 0
|
||||
&& player.getEntityBoundingBox().grow(EXTRA_HIT_MARGIN).intersects(attacker.getEntityBoundingBox())){
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,21 +1,17 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.data.IStoredVariable;
|
||||
import electroblob.wizardry.data.Persistence;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.misc.WizardryPathFinder;
|
||||
import electroblob.wizardry.packet.PacketClairvoyance;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.WizardryPathFinder;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.monster.EntityZombie;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -39,23 +35,30 @@ public class Clairvoyance extends Spell {
|
||||
/** The number of ticks it takes each path particle to move from one path point to the next. */
|
||||
public static final int PARTICLE_MOVEMENT_INTERVAL = 45;
|
||||
|
||||
public static final IStoredVariable<BlockPos> LOCATION_KEY = IStoredVariable.StoredVariable.ofBlockPos("clairvoyancePos", Persistence.ALWAYS);
|
||||
public static final IStoredVariable<Integer> DIMENSION_KEY = IStoredVariable.StoredVariable.ofInt("clairvoyanceDimension", Persistence.ALWAYS);
|
||||
|
||||
public Clairvoyance(){
|
||||
super("clairvoyance", Tier.APPRENTICE, Element.SORCERY, SpellType.UTILITY, 20, 100, EnumAction.BOW, false);
|
||||
super("clairvoyance", EnumAction.BOW, false);
|
||||
addProperties(RANGE, DURATION);
|
||||
WizardData.registerStoredVariables(LOCATION_KEY, DIMENSION_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesSpellRequirePacket(){
|
||||
return false;
|
||||
}
|
||||
@Override public boolean canBeCastByNPCs() { return false; }
|
||||
@Override public boolean canBeCastByDispensers() { return false; }
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
WizardData properties = WizardData.get(caster);
|
||||
WizardData data = WizardData.get(caster);
|
||||
|
||||
if(properties != null && !caster.isSneaking()){
|
||||
if(caster.dimension == properties.getClairvoyanceDimension()){
|
||||
if(properties.getClairvoyanceLocation() != null){
|
||||
if(data != null && !caster.isSneaking()){
|
||||
|
||||
Integer dimension = data.getVariable(DIMENSION_KEY);
|
||||
BlockPos location = data.getVariable(LOCATION_KEY);
|
||||
|
||||
if(dimension != null && caster.dimension == dimension){
|
||||
if(location != null){
|
||||
|
||||
if(!world.isRemote) caster.sendStatusMessage(new TextComponentTranslation("spell." + this.getUnlocalisedName() + ".searching"), true);
|
||||
|
||||
@@ -66,16 +69,15 @@ public class Clairvoyance extends Spell {
|
||||
}
|
||||
};
|
||||
arbitraryZombie.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE)
|
||||
.setBaseValue(256 * modifiers.get(WizardryItems.range_upgrade));
|
||||
.setBaseValue(getProperty(RANGE).doubleValue() * modifiers.get(WizardryItems.range_upgrade));
|
||||
arbitraryZombie.setPosition(caster.posX, caster.posY, caster.posZ);
|
||||
arbitraryZombie.setPathPriority(PathNodeType.WATER, 0.0F);
|
||||
arbitraryZombie.onGround = true;
|
||||
|
||||
BlockPos destination = properties.getClairvoyanceLocation();
|
||||
|
||||
WizardryPathFinder pathfinder = new WizardryPathFinder(arbitraryZombie.getNavigator().getNodeProcessor());
|
||||
|
||||
Path path = pathfinder.findPath(world, arbitraryZombie, destination, 256 * modifiers.get(WizardryItems.range_upgrade));
|
||||
Path path = pathfinder.findPath(world, arbitraryZombie, location,
|
||||
getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade));
|
||||
|
||||
if(path != null && path.getFinalPathPoint() != null){
|
||||
|
||||
@@ -83,9 +85,9 @@ public class Clairvoyance extends Spell {
|
||||
int y = path.getFinalPathPoint().y;
|
||||
int z = path.getFinalPathPoint().z;
|
||||
|
||||
if(x == destination.getX() && y == destination.getY() && z == destination.getZ()){
|
||||
if(x == location.getX() && y == location.getY() && z == location.getZ()){
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
if(!world.isRemote && caster instanceof EntityPlayerMP){
|
||||
WizardryPacketHandler.net.sendTo(new PacketClairvoyance.Message(path, modifiers.get(WizardryItems.duration_upgrade)),
|
||||
@@ -107,13 +109,16 @@ public class Clairvoyance extends Spell {
|
||||
}
|
||||
|
||||
// Fixes the problem with the sound not playing for the client of the caster.
|
||||
if(world.isRemote) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f);
|
||||
if(world.isRemote) this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void spawnPathPaticles(World world, Path path, float durationMultiplier){
|
||||
|
||||
// A bit annoying that we have to use the reference here but there's no easy way around it
|
||||
float duration = Spells.clairvoyance.getProperty(DURATION).floatValue();
|
||||
|
||||
PathPoint point, nextPoint;
|
||||
|
||||
while(!path.isFinished()){
|
||||
@@ -127,7 +132,7 @@ public class Clairvoyance extends Spell {
|
||||
(nextPoint.x - point.x) / (float)PARTICLE_MOVEMENT_INTERVAL,
|
||||
(nextPoint.y - point.y) / (float)PARTICLE_MOVEMENT_INTERVAL,
|
||||
(nextPoint.z - point.z) / (float)PARTICLE_MOVEMENT_INTERVAL)
|
||||
.time((int)(1800 * durationMultiplier)).clr(0, 1, 0.3f).spawn(world);
|
||||
.time((int)(duration * durationMultiplier)).clr(0, 1, 0.3f).spawn(world);
|
||||
|
||||
path.incrementPathIndex();
|
||||
path.incrementPathIndex();
|
||||
@@ -136,7 +141,7 @@ public class Clairvoyance extends Spell {
|
||||
point = path.getFinalPathPoint();
|
||||
|
||||
ParticleBuilder.create(Type.PATH).pos(point.x + 0.5, point.y + 0.5, point.z + 0.5)
|
||||
.time((int)(1800 * durationMultiplier)).clr(1, 1, 1).spawn(world);
|
||||
.time((int)(duration * durationMultiplier)).clr(1f, 1f, 1f).spawn(world);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
@@ -145,17 +150,19 @@ public class Clairvoyance extends Spell {
|
||||
if(event.getEntityPlayer().isSneaking()){
|
||||
|
||||
// The event now has an ItemStack, which greatly simplifies hand-related stuff.
|
||||
ItemStack wand = event.getItemStack();
|
||||
ItemStack stack = event.getItemStack();
|
||||
|
||||
if(wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Clairvoyance){
|
||||
if(stack.getItem() instanceof ISpellCastingItem
|
||||
&& ((ISpellCastingItem)stack.getItem()).getCurrentSpell(stack) instanceof Clairvoyance){
|
||||
|
||||
WizardData properties = WizardData.get(event.getEntityPlayer());
|
||||
WizardData data = WizardData.get(event.getEntityPlayer());
|
||||
|
||||
if(data != null){
|
||||
|
||||
if(properties != null){
|
||||
// THIS is why BlockPos is a thing - in 1.7.10 this requires a clumsy switch statement.
|
||||
BlockPos pos = event.getPos().offset(event.getFace());
|
||||
|
||||
properties.setClairvoyancePoint(pos, event.getWorld().provider.getDimension());
|
||||
data.setVariable(LOCATION_KEY, pos);
|
||||
data.setVariable(DIMENSION_KEY, event.getWorld().provider.getDimension());
|
||||
|
||||
if(!event.getWorld().isRemote){
|
||||
event.getEntityPlayer().sendStatusMessage(
|
||||
|
||||
@@ -1,63 +1,58 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.tileentity.TileEntityTimer;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Cobwebs extends SpellRay {
|
||||
|
||||
private static final int BASE_DURATION = 400;
|
||||
|
||||
public Cobwebs(){
|
||||
super("cobwebs", Tier.ADVANCED, Element.EARTH, SpellType.ATTACK, 30, 70, false, 12, SoundEvents.BLOCK_LAVA_EXTINGUISH);
|
||||
this.ignoreEntities(true);
|
||||
super("cobwebs", false, EnumAction.NONE);
|
||||
this.ignoreLivingEntities(true);
|
||||
addProperties(EFFECT_RADIUS, DURATION);
|
||||
}
|
||||
|
||||
@Override public boolean doesSpellRequirePacket(){ return false; }
|
||||
@Override public boolean requiresPacket(){ return false; }
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
boolean flag = false;
|
||||
|
||||
pos = pos.offset(side);
|
||||
|
||||
if(world.isAirBlock(pos)){
|
||||
if(!world.isRemote){
|
||||
world.setBlockState(pos, WizardryBlocks.vanishing_cobweb.getDefaultState());
|
||||
if(world.getTileEntity(pos) instanceof TileEntityTimer){
|
||||
((TileEntityTimer)world.getTileEntity(pos))
|
||||
.setLifetime((int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
}
|
||||
}
|
||||
flag = true;
|
||||
}
|
||||
int blastUpgradeCount = (int)((modifiers.get(WizardryItems.blast_upgrade) - 1) / Constants.RANGE_INCREASE_PER_LEVEL + 0.5f);
|
||||
|
||||
for(EnumFacing facing : EnumFacing.values()){
|
||||
float radius = getProperty(EFFECT_RADIUS).floatValue() + 0.73f * blastUpgradeCount;
|
||||
|
||||
BlockPos pos1 = pos.offset(facing);
|
||||
List<BlockPos> sphere = WizardryUtilities.getBlockSphere(pos, radius * modifiers.get(WizardryItems.blast_upgrade));
|
||||
|
||||
for(BlockPos pos1 : sphere){
|
||||
|
||||
if(world.isAirBlock(pos1)){
|
||||
if(!world.isRemote){
|
||||
world.setBlockState(pos1, WizardryBlocks.vanishing_cobweb.getDefaultState());
|
||||
if(world.getTileEntity(pos1) instanceof TileEntityTimer){
|
||||
((TileEntityTimer)world.getTileEntity(pos1))
|
||||
.setLifetime((int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
.setLifetime((int)(getProperty(DURATION).doubleValue()
|
||||
* modifiers.get(WizardryItems.duration_upgrade)));
|
||||
}
|
||||
}
|
||||
flag = true;
|
||||
@@ -68,7 +63,7 @@ public class Cobwebs extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,22 +1,28 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import electroblob.wizardry.item.IConjuredItem;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class ConjureArmour extends SpellConjuration {
|
||||
|
||||
private static final Map<EntityEquipmentSlot, Item> SPECTRAL_ARMOUR_MAP = ImmutableMap.of(
|
||||
EntityEquipmentSlot.HEAD, WizardryItems.spectral_helmet,
|
||||
EntityEquipmentSlot.CHEST, WizardryItems.spectral_chestplate,
|
||||
EntityEquipmentSlot.LEGS, WizardryItems.spectral_leggings,
|
||||
EntityEquipmentSlot.FEET, WizardryItems.spectral_boots);
|
||||
|
||||
public ConjureArmour(){
|
||||
super("conjure_armour", Tier.ADVANCED, Element.HEALING, SpellType.DEFENCE, 45, 50, null, WizardrySounds.SPELL_CONJURATION);
|
||||
super("conjure_armour", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -29,9 +35,9 @@ public class ConjureArmour extends SpellConjuration {
|
||||
for(EntityEquipmentSlot slot : WizardryUtilities.ARMOUR_SLOTS){
|
||||
|
||||
if(caster.getItemStackFromSlot(slot).isEmpty() &&
|
||||
!WizardryUtilities.doesPlayerHaveItem(caster, WizardryItems.SPECTRAL_ARMOUR_MAP.get(slot))){
|
||||
!WizardryUtilities.doesPlayerHaveItem(caster, SPECTRAL_ARMOUR_MAP.get(slot))){
|
||||
|
||||
armour = new ItemStack(WizardryItems.SPECTRAL_ARMOUR_MAP.get(slot));
|
||||
armour = new ItemStack(SPECTRAL_ARMOUR_MAP.get(slot));
|
||||
IConjuredItem.setDurationMultiplier(armour, modifiers.get(WizardryItems.duration_upgrade));
|
||||
// Sets a blank "ench" tag to trick the renderer into showing the enchantment effect on the armour model
|
||||
armour.getTagCompound().setTag("ench", new NBTTagList());
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.tileentity.TileEntityTimer;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class ConjureBlock extends SpellRay {
|
||||
|
||||
private static final String BLOCK_LIFETIME = "block_lifetime";
|
||||
|
||||
public ConjureBlock(){
|
||||
super("conjure_block", false, EnumAction.NONE);
|
||||
this.ignoreLivingEntities(true);
|
||||
addProperties(BLOCK_LIFETIME);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(world.getBlockState(pos).getBlock() == WizardryBlocks.spectral_block){
|
||||
|
||||
if(!world.isRemote){
|
||||
// Dispelling of blocks
|
||||
world.setBlockToAir(pos);
|
||||
}else{
|
||||
ParticleBuilder.create(Type.FLASH).pos(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5).scale(3)
|
||||
.clr(0.75f, 1, 0.85f).spawn(world);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pos = pos.offset(side);
|
||||
|
||||
if(world.isRemote){
|
||||
ParticleBuilder.create(Type.FLASH).pos(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5).scale(3)
|
||||
.clr(0.75f, 1, 0.85f).spawn(world);
|
||||
}
|
||||
|
||||
if(WizardryUtilities.canBlockBeReplaced(world, pos)){
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
world.setBlockState(pos, WizardryBlocks.spectral_block.getDefaultState());
|
||||
|
||||
if(world.getTileEntity(pos) instanceof TileEntityTimer){
|
||||
((TileEntityTimer)world.getTileEntity(pos)).setLifetime((int)(getProperty(BLOCK_LIFETIME).floatValue()
|
||||
* modifiers.get(WizardryItems.duration_upgrade)));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Containment extends SpellRay {
|
||||
|
||||
public Containment(){
|
||||
super("containment", false, EnumAction.NONE);
|
||||
this.soundValues(1, 1, 0.2f);
|
||||
addProperties(EFFECT_DURATION, EFFECT_STRENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
((EntityLivingBase)target).addPotionEffect(new PotionEffect(WizardryPotions.containment,
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)),
|
||||
getProperty(EFFECT_STRENGTH).intValue() + SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY))));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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 true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void spawnParticle(World world, double x, double y, double z, double vx, double vy, double vz){
|
||||
ParticleBuilder.create(Type.PATH).pos(x, y, z).clr(0x7988cc).time(20 + world.rand.nextInt(8)).spawn(world);
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).time(12 + world.rand.nextInt(8)).clr(1f, 1f, 1f).spawn(world);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,25 +1,36 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
|
||||
public class CureEffects extends SpellBuff {
|
||||
|
||||
public CureEffects(){
|
||||
super("cure_effects", Tier.APPRENTICE, Element.HEALING, SpellType.DEFENCE, 25, 40, WizardrySounds.SPELL_HEAL, 0.8f, 0.8f, 1);
|
||||
super("cure_effects", 0.8f, 0.8f, 1);
|
||||
this.soundValues(0.7f, 1.2f, 0.4f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean applyEffects(EntityLivingBase caster, SpellModifiers modifiers){
|
||||
|
||||
|
||||
if(!caster.getActivePotionEffects().isEmpty()){
|
||||
caster.clearActivePotions();
|
||||
return true;
|
||||
|
||||
ItemStack milk = new ItemStack(Items.MILK_BUCKET);
|
||||
|
||||
boolean flag = false;
|
||||
|
||||
for(PotionEffect effect : caster.getActivePotionEffects()){
|
||||
// The PotionEffect version (as opposed to Potion) does not call cleanup callbacks
|
||||
if(effect.isCurativeItem(milk)){
|
||||
caster.removePotionEffect(effect.getPotion());
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class CurseOfEnfeeblement extends SpellRay {
|
||||
|
||||
public CurseOfEnfeeblement(){
|
||||
super("curse_of_enfeeblement", false, EnumAction.NONE);
|
||||
this.soundValues(1, 1.1f, 0.2f);
|
||||
addProperties(EFFECT_STRENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
// This will actually run out in the end, but only if you leave Minecraft running for 3.4 years
|
||||
((EntityLivingBase)target).addPotionEffect(new PotionEffect(WizardryPotions.curse_of_enfeeblement,
|
||||
Integer.MAX_VALUE, getProperty(EFFECT_STRENGTH).intValue() + SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY))));
|
||||
// Reduce the target's health to its new max health if necessary
|
||||
if(((EntityLivingBase)target).getHealth() > ((EntityLivingBase)target).getMaxHealth()){
|
||||
target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, MagicDamage.DamageType.WITHER),
|
||||
((EntityLivingBase)target).getHealth() - ((EntityLivingBase)target).getMaxHealth());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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 true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void spawnParticle(World world, double x, double y, double z, double vx, double vy, double vz){
|
||||
ParticleBuilder.create(Type.DARK_MAGIC).pos(x, y, z).clr(0.2f, 0, 0.3f).spawn(world);
|
||||
ParticleBuilder.create(Type.DARK_MAGIC).pos(x, y, z).clr(0.1f, 0, 0).spawn(world);
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).time(12 + world.rand.nextInt(8)).clr(0.4f, 0, 0).spawn(world);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,54 +1,80 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.util.IElementalDamage;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.data.IStoredVariable;
|
||||
import electroblob.wizardry.data.Persistence;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.integration.DamageSafetyChecker;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.*;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.nbt.NBTUtil;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingHurtEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class CurseOfSoulbinding extends SpellRay {
|
||||
|
||||
public static final IStoredVariable<Set<UUID>> TARGETS_KEY = new IStoredVariable.StoredVariable<>("soulboundCreatures",
|
||||
s -> NBTExtras.listToNBT(s, NBTUtil::createUUIDTag),
|
||||
// For some reason gradle screams at me unless I explicitly declare the type of t here, despite IntelliJ being fine without it
|
||||
(NBTTagList t) -> new HashSet<>(NBTExtras.NBTToList(t, NBTUtil::getUUIDFromTag)),
|
||||
// Curse of soulbinding is lifted when the caster dies, but not when they switch dimensions.
|
||||
Persistence.DIMENSION_CHANGE);
|
||||
|
||||
public CurseOfSoulbinding(){
|
||||
super("curse_of_soulbinding", Tier.ADVANCED, Element.NECROMANCY, SpellType.ATTACK, 35, 100, false, 10, SoundEvents.ENTITY_WITHER_SPAWN);
|
||||
super("curse_of_soulbinding", false, EnumAction.NONE);
|
||||
this.soundValues(1, 1.1f, 0.2f);
|
||||
WizardData.registerStoredVariables(TARGETS_KEY);
|
||||
}
|
||||
|
||||
|
||||
@Override public boolean canBeCastByNPCs() { return false; }
|
||||
// You can't damage a dispenser so this would be nonsense!
|
||||
@Override public boolean canBeCastByDispensers() { return false; }
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target) && caster instanceof EntityPlayer
|
||||
&& WizardData.get((EntityPlayer)caster) != null){
|
||||
// Return false if soulbinding failed (e.g. if the target is already soulbound)
|
||||
if(!WizardData.get((EntityPlayer)caster).soulbind((EntityLivingBase)target)) return false;
|
||||
if(WizardryUtilities.isLiving(target) && caster instanceof EntityPlayer){
|
||||
WizardData data = WizardData.get((EntityPlayer)caster);
|
||||
if(data != null){
|
||||
// Return false if soulbinding failed (e.g. if the target is already soulbound)
|
||||
if(getSoulboundCreatures(data).add(target.getUniqueID())){
|
||||
// This will actually run out in the end, but only if you leave Minecraft running for 3.4 years
|
||||
((EntityLivingBase)target).addPotionEffect(new PotionEffect(WizardryPotions.curse_of_soulbinding, Integer.MAX_VALUE));
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
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, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -65,11 +91,41 @@ public class CurseOfSoulbinding extends SpellRay {
|
||||
if(!event.getEntity().world.isRemote && event.getEntityLiving() instanceof EntityPlayer
|
||||
&& !event.getSource().isUnblockable() && !(event.getSource() instanceof IElementalDamage
|
||||
&& ((IElementalDamage)event.getSource()).isRetaliatory())){
|
||||
WizardData data = WizardData.get((EntityPlayer)event.getEntityLiving());
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getEntityLiving();
|
||||
WizardData data = WizardData.get(player);
|
||||
|
||||
if(data != null){
|
||||
data.damageAllSoulboundCreatures(event.getAmount(), event.getSource().getDamageType());
|
||||
|
||||
for(Iterator<UUID> iterator = getSoulboundCreatures(data).iterator(); iterator.hasNext();){
|
||||
|
||||
Entity entity = WizardryUtilities.getEntityByUUID(player.world, iterator.next());
|
||||
|
||||
if(entity == null) iterator.remove();
|
||||
|
||||
if(entity instanceof EntityLivingBase){
|
||||
// Retaliatory effect
|
||||
if(DamageSafetyChecker.attackEntitySafely(entity, MagicDamage.causeDirectMagicDamage(player,
|
||||
MagicDamage.DamageType.MAGIC, true), event.getAmount(), event.getSource().getDamageType(),
|
||||
DamageSource.MAGIC, false)){
|
||||
// Sound only plays if the damage succeeds
|
||||
entity.playSound(WizardrySounds.SPELL_CURSE_OF_SOULBINDING_RETALIATE, 1.0F, player.world.rand.nextFloat() * 0.2F + 1.0F);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static Set<UUID> getSoulboundCreatures(WizardData data){
|
||||
|
||||
if(data.getVariable(TARGETS_KEY) == null){
|
||||
Set<UUID> result = new HashSet<>();
|
||||
data.setVariable(TARGETS_KEY, result);
|
||||
return result;
|
||||
|
||||
}else return data.getVariable(TARGETS_KEY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class CurseOfUndeath extends SpellRay {
|
||||
|
||||
public CurseOfUndeath(){
|
||||
super("curse_of_undeath", false, EnumAction.NONE);
|
||||
this.soundValues(1, 1.1f, 0.2f);
|
||||
addProperties(EFFECT_STRENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
// This will actually run out in the end, but only if you leave Minecraft running for 3.4 years
|
||||
((EntityLivingBase)target).addPotionEffect(new PotionEffect(WizardryPotions.curse_of_undeath, Integer.MAX_VALUE,
|
||||
getProperty(EFFECT_STRENGTH).intValue() + SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY))));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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 true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void spawnParticle(World world, double x, double y, double z, double vx, double vy, double vz){
|
||||
ParticleBuilder.create(Type.DARK_MAGIC).pos(x, y, z).clr(0x686c00).spawn(world);
|
||||
ParticleBuilder.create(Type.DARK_MAGIC).pos(x, y, z).clr(0x251609).spawn(world);
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).time(12 + world.rand.nextInt(8)).clr(0xe6e592).spawn(world);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,43 +1,43 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.construct.EntityDecay;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Decay extends SpellConstructRanged<EntityDecay> {
|
||||
|
||||
private static final int BASE_SPAWN_COUNT = 5;
|
||||
|
||||
public static final String DECAY_PATCHES_SPAWNED = "decay_patches_spawned";
|
||||
|
||||
public Decay(){
|
||||
super("decay", Tier.ADVANCED, Element.NECROMANCY, SpellType.ATTACK, 50, 200, EntityDecay::new, 400, 12, SoundEvents.ENTITY_WITHER_SHOOT);
|
||||
super("decay", EntityDecay::new, false);
|
||||
this.soundValues(1, 1.1f, 0.1f);
|
||||
this.floor(true);
|
||||
this.overlap(true);
|
||||
addProperties(DECAY_PATCHES_SPAWNED, EFFECT_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean spawnConstruct(World world, double x, double y, double z, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
protected boolean spawnConstruct(World world, double x, double y, double z, EnumFacing side, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
|
||||
if(world.getBlockState(new BlockPos(x, y, z)).isNormalCube()) return false;
|
||||
|
||||
super.spawnConstruct(world, x, y, z, caster, modifiers);
|
||||
|
||||
int quantity = (int)(BASE_SPAWN_COUNT * modifiers.get(WizardryItems.blast_upgrade));
|
||||
int horizontalRange = (int)(2 * modifiers.get(WizardryItems.blast_upgrade));
|
||||
super.spawnConstruct(world, x, y, z, side, caster, modifiers);
|
||||
|
||||
float decayCount = getProperty(DECAY_PATCHES_SPAWNED).floatValue();
|
||||
int quantity = (int)(decayCount * modifiers.get(WizardryItems.blast_upgrade));
|
||||
// If there are more decay patches, they need more space to spawn in
|
||||
int horizontalRange = (int)(0.4 * decayCount * modifiers.get(WizardryItems.blast_upgrade));
|
||||
int verticalRange = (int)(6 * modifiers.get(WizardryItems.blast_upgrade));
|
||||
|
||||
for(int i=0; i<quantity; i++){
|
||||
BlockPos pos = WizardryUtilities.findNearbyFloorSpace(caster, horizontalRange, verticalRange);
|
||||
if(pos == null) break;
|
||||
super.spawnConstruct(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, caster, modifiers);
|
||||
super.spawnConstruct(world, pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5, side, caster, modifiers);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.living.EntityDecoy;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
@@ -16,8 +12,13 @@ import net.minecraft.world.World;
|
||||
|
||||
public class Decoy extends Spell {
|
||||
|
||||
public static final String DECOY_LIFETIME = "decoy_lifetime";
|
||||
public static final String MOB_TRICK_CHANCE = "mob_trick_chance";
|
||||
|
||||
public Decoy(){
|
||||
super("decoy", Tier.ADVANCED, Element.SORCERY, SpellType.UTILITY, 40, 200, EnumAction.BOW, false);
|
||||
super("decoy", EnumAction.BOW, false);
|
||||
this.soundValues(1, 0.9f, 0.2f);
|
||||
addProperties(DECOY_LIFETIME, MOB_TRICK_CHANCE);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return true; }
|
||||
@@ -28,7 +29,7 @@ public class Decoy extends Spell {
|
||||
// Uses the synchronised entity id to ensure it is consistent on client and server, but not always the same.
|
||||
double splitSpeed = caster.getEntityId() % 2 == 0 ? 0.3 : -0.3;
|
||||
spawnDecoy(world, caster, modifiers, splitSpeed);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F));
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -38,7 +39,7 @@ public class Decoy extends Spell {
|
||||
// Determines whether the caster moves left and the decoy moves right, or vice versa.
|
||||
double splitSpeed = world.rand.nextBoolean() ? 0.3 : -0.3;
|
||||
spawnDecoy(world, caster, modifiers, splitSpeed);
|
||||
caster.playSound(WizardrySounds.SPELL_CONJURATION, 1.0F, 0.4F / (world.rand.nextFloat() * 0.4F + 0.8F));
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -47,7 +48,7 @@ public class Decoy extends Spell {
|
||||
if(!world.isRemote){
|
||||
EntityDecoy decoy = new EntityDecoy(world);
|
||||
decoy.setCaster(caster);
|
||||
decoy.setLifetime(600);
|
||||
decoy.setLifetime(getProperty(DECOY_LIFETIME).intValue());
|
||||
decoy.setLocationAndAngles(caster.posX, caster.posY, caster.posZ, caster.rotationYaw, caster.rotationPitch);
|
||||
decoy.addVelocity(-caster.getLookVec().z * splitSpeed, 0, caster.getLookVec().x * splitSpeed);
|
||||
// Ignores the show names setting, since this would allow a player to easily detect a decoy
|
||||
@@ -58,9 +59,11 @@ public class Decoy extends Spell {
|
||||
// Tricks any mobs that are targeting the caster into targeting the decoy instead.
|
||||
for(EntityLiving creature : WizardryUtilities.getEntitiesWithinRadius(16, caster.posX, caster.posY,
|
||||
caster.posZ, world, EntityLiving.class)){
|
||||
// More likely to trick mobs the higher the damage multiplier. Starts off at 50%.
|
||||
if(world.rand.nextInt((int)(6 * modifiers.get(SpellModifiers.POTENCY))) < 3){
|
||||
if(creature.getAttackTarget() == caster) creature.setAttackTarget(decoy);
|
||||
// More likely to trick mobs the higher the damage multiplier
|
||||
// The default base value is 0.5, so modifiers of 2 or more will guarantee mobs are tricked
|
||||
if(creature.getAttackTarget() == caster && world.rand.nextFloat()
|
||||
< getProperty(MOB_TRICK_CHANCE).floatValue() * modifiers.get(SpellModifiers.POTENCY)){
|
||||
creature.setAttackTarget(decoy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
@@ -12,40 +7,45 @@ import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Detonate extends SpellRay {
|
||||
|
||||
private static final double BASE_RADIUS = 3;
|
||||
|
||||
// More descriptive/accurate than just "damage"
|
||||
public static final String MAX_DAMAGE = "max_damage";
|
||||
|
||||
public Detonate(){
|
||||
super("detonate", Tier.ADVANCED, Element.FIRE, SpellType.ATTACK, 45, 50, false, 16, SoundEvents.ENTITY_GENERIC_EXPLODE);
|
||||
super("detonate", false, EnumAction.NONE);
|
||||
this.soundValues(4, 0.7f, 0.14f);
|
||||
this.ignoreEntities(true);
|
||||
this.ignoreLivingEntities(true);
|
||||
addProperties(MAX_DAMAGE, BLAST_RADIUS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(
|
||||
BASE_RADIUS * modifiers.get(WizardryItems.blast_upgrade), pos.getX(), pos.getY(), pos.getZ(), world);
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(getProperty(BLAST_RADIUS).doubleValue()
|
||||
* modifiers.get(WizardryItems.blast_upgrade), pos.getX(), pos.getY(), pos.getZ(), world);
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.BLAST),
|
||||
// Damage decreases with distance but cannot be less than 0, naturally.
|
||||
Math.max(12.0f - (float)target.getDistance(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5)
|
||||
* 4, 0) * modifiers.get(SpellModifiers.POTENCY));
|
||||
Math.max(getProperty(MAX_DAMAGE).floatValue() - (float)target.getDistance(pos.getX() + 0.5,
|
||||
pos.getY() + 0.5, pos.getZ() + 0.5) * 4, 0) * modifiers.get(SpellModifiers.POTENCY));
|
||||
}
|
||||
|
||||
}else{
|
||||
@@ -56,7 +56,7 @@ public class Detonate extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.entity.projectile.EntityEmber;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Disintegration extends SpellRay {
|
||||
|
||||
public static final String EMBER_COUNT = "ember_count";
|
||||
public static final String EMBER_LIFETIME = "ember_lifetime";
|
||||
|
||||
public Disintegration(){
|
||||
super("disintegration", false, EnumAction.NONE);
|
||||
addProperties(DAMAGE, BURN_DURATION, EMBER_LIFETIME, EMBER_COUNT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(MagicDamage.isEntityImmune(DamageType.FIRE, target)){
|
||||
if(!world.isRemote && caster instanceof EntityPlayer) ((EntityPlayer)caster).sendStatusMessage(
|
||||
new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted()), true);
|
||||
}else{
|
||||
|
||||
target.setFire((int)(getProperty(BURN_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
WizardryUtilities.attackEntityWithoutKnockback(target, caster == null ? DamageSource.MAGIC :
|
||||
MagicDamage.causeDirectMagicDamage(caster, DamageType.FIRE),
|
||||
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
if(!world.isRemote && target instanceof EntityLivingBase && ((EntityLivingBase)target).getHealth() <= 0){
|
||||
spawnEmbers(world, caster, target, getProperty(EMBER_COUNT).intValue());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void spawnEmbers(World world, EntityLivingBase caster, Entity target, int count){
|
||||
|
||||
for(int i = 0; i < count; i++){
|
||||
EntityEmber ember = new EntityEmber(world, caster);
|
||||
double x = (world.rand.nextDouble() - 0.5) * target.width;
|
||||
double y = world.rand.nextDouble() * target.height;
|
||||
double z = (world.rand.nextDouble() - 0.5) * target.width;
|
||||
ember.setPosition(target.posX + x, target.posY + y, target.posZ + z);
|
||||
float speed = 0.2f;
|
||||
ember.setVelocity(x * speed, y * 0.5f * speed, z * speed);
|
||||
world.spawnEntity(ember);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
for(int i = 0; i < 8; i++){
|
||||
world.spawnParticle(EnumParticleTypes.LAVA, hit.x, hit.y, hit.z, 0, 0, 0);
|
||||
}
|
||||
|
||||
if(world.getBlockState(pos).getMaterial().isSolid()){
|
||||
Vec3d vec = hit.add(new Vec3d(side.getDirectionVec()).scale(WizardryUtilities.ANTI_Z_FIGHTING_OFFSET));
|
||||
ParticleBuilder.create(Type.SCORCH).pos(vec).face(side).clr(1, 0.2f, 0).spawn(world);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void spawnParticleRay(World world, Vec3d origin, Vec3d direction, EntityLivingBase caster, double distance){
|
||||
Vec3d endpoint = origin.add(direction.scale(distance));
|
||||
ParticleBuilder.create(Type.BEAM).clr(1, 0.4f, 0).fade(1, 0.1f, 0).time(4).pos(origin).target(endpoint).spawn(world);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.block.BlockCrystalOre;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.RelativeFacing;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockOre;
|
||||
import net.minecraft.block.BlockRedstoneOre;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.FurnaceRecipes;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class Divination extends Spell {
|
||||
|
||||
private static final float NUDGE_SPEED = 0.2f;
|
||||
|
||||
public Divination(){
|
||||
super("divination", EnumAction.NONE, false);
|
||||
addProperties(RANGE);
|
||||
}
|
||||
|
||||
/** A set of constants representing the different 'signal strengths' for the divination spell. In practical
|
||||
* terms, this means different chat readouts and effects. */
|
||||
protected enum Strength {
|
||||
|
||||
NOTHING("nothing", -1),
|
||||
WEAK("weak", 0),
|
||||
MODERATE("moderate", 0.25f),
|
||||
STRONG("strong", 0.5f),
|
||||
VERY_STRONG("very_strong", 0.75f);
|
||||
|
||||
String key;
|
||||
float minWeight;
|
||||
|
||||
Strength(String key, float minWeight){
|
||||
this.key = key;
|
||||
this.minWeight = minWeight;
|
||||
}
|
||||
|
||||
protected static Strength forWeight(float weight){
|
||||
return Arrays.stream(values()).filter(s -> s.minWeight < weight).max(Comparator.naturalOrder()).orElse(NOTHING);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
double range = getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade);
|
||||
|
||||
List<BlockPos> sphere = WizardryUtilities.getBlockSphere(caster.getPosition(), range);
|
||||
|
||||
sphere.removeIf(b -> !(world.getBlockState(b).getBlock() instanceof BlockOre
|
||||
|| world.getBlockState(b).getBlock() instanceof BlockRedstoneOre
|
||||
|| world.getBlockState(b).getBlock() instanceof BlockCrystalOre
|
||||
|| Arrays.asList(Wizardry.settings.divinationOreWhitelist)
|
||||
.contains(world.getBlockState(b).getBlock().getRegistryName())));
|
||||
|
||||
Strength strength = Strength.NOTHING;
|
||||
|
||||
EnumFacing direction = EnumFacing.DOWN; // Doesn't matter what this is
|
||||
|
||||
if(!sphere.isEmpty()){
|
||||
|
||||
// Sorts the positions based on weight (see below), in ascending order
|
||||
sphere.sort(Comparator.comparingDouble(b -> calculateWeight(world, caster, b, range, modifiers)));
|
||||
|
||||
// The weights are sorted in ascending order, so this must be the largest
|
||||
BlockPos target = sphere.get(sphere.size() - 1);
|
||||
|
||||
direction = EnumFacing.getFacingFromVector((float)(target.getX() + 0.5 - caster.posX),
|
||||
(float)(target.getY() + 0.5 - (caster.getEntityBoundingBox().minY + caster.getEyeHeight())),
|
||||
(float)(target.getZ() + 0.5 - caster.posZ));
|
||||
|
||||
if(world.isRemote) ParticleBuilder.create(ParticleBuilder.Type.SPARKLE).pos(target.getX() + 0.5,
|
||||
target.getY() + 1.5, target.getZ() + 0.5).spawn(world);
|
||||
|
||||
strength = Strength.forWeight(calculateWeight(world, caster, target, range, modifiers));
|
||||
}
|
||||
|
||||
if(!world.isRemote){
|
||||
caster.sendStatusMessage(new TextComponentTranslation("spell." + this.getUnlocalisedName() + "."
|
||||
+ strength.key, new TextComponentTranslation("spell." + this.getUnlocalisedName() + "."
|
||||
+ RelativeFacing.relativise(direction, caster).name)), false);
|
||||
}else{
|
||||
switch(strength){
|
||||
case NOTHING: break;
|
||||
case WEAK: break;
|
||||
case MODERATE:
|
||||
spawnHintParticles(world, caster, 3, direction);
|
||||
break;
|
||||
case STRONG:
|
||||
spawnHintParticles(world, caster, 8, direction);
|
||||
break;
|
||||
case VERY_STRONG:
|
||||
spawnHintParticles(world, caster, 12, direction);
|
||||
caster.addVelocity(direction.getXOffset() * NUDGE_SPEED, direction.getYOffset() * NUDGE_SPEED,
|
||||
direction.getZOffset() * NUDGE_SPEED);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void spawnHintParticles(World world, Entity caster, int count, EnumFacing direction){
|
||||
|
||||
Vec3d vec = new Vec3d(caster.getPosition().offset(EnumFacing.UP).offset(direction, 2)).add(0.5, 0.5, 0.5);
|
||||
|
||||
for(int i=0; i<count; i++){
|
||||
ParticleBuilder.create(ParticleBuilder.Type.FLASH, world.rand, vec.x, vec.y, vec.z, 0.7, false)
|
||||
.time(20 + world.rand.nextInt(5)).clr(0.6f + world.rand.nextFloat() * 0.4f,
|
||||
0.6f + world.rand.nextFloat() * 0.4f, 0.6f + world.rand.nextFloat() * 0.4f).scale(0.3f).spawn(world);
|
||||
}
|
||||
}
|
||||
|
||||
protected static float calculateWeight(World world, EntityPlayer caster, BlockPos pos, double range, SpellModifiers modifiers){
|
||||
|
||||
Block block = world.getBlockState(pos).getBlock();
|
||||
// On a non-sorcery wand, the value of the ore has no effect on its weight
|
||||
float weightModifier = modifiers.get(SpellModifiers.POTENCY) - 1;
|
||||
|
||||
// xp is a decent way of determining the 'value' of a block
|
||||
// There is a degree of randomness associated with it though...
|
||||
float xp = block.getExpDrop(world.getBlockState(pos), world, pos, 0);
|
||||
// For some reason smelting gives a lot less than mining, hence the multiplying by 4
|
||||
if(xp == 0) xp = 4 * FurnaceRecipes.instance().getSmeltingExperience(new ItemStack(block));
|
||||
|
||||
// By my (rather rough) calculations, this should mean that using a master sorcerer wand, an iron ore block
|
||||
// and a diamond ore block just under twice as far away as the iron ore should have about the same weight
|
||||
return (float)(1 - caster.getDistance(pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5)/range + 0.2 * weightModifier * xp);
|
||||
}
|
||||
|
||||
}
|
||||
+27
-21
@@ -1,29 +1,27 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntitySmallFireball;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.entity.projectile.EntityDragonFireball;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Fireball extends Spell {
|
||||
public class DragonFireball extends Spell {
|
||||
|
||||
public Fireball(){
|
||||
super("fireball", Tier.APPRENTICE, Element.FIRE, SpellType.ATTACK, 10, 15, EnumAction.NONE, false);
|
||||
// Does 2.5 hearts of damage and 5 seconds of fire, for reference.
|
||||
public static final String ACCELERATION = "acceleration";
|
||||
|
||||
public DragonFireball(){
|
||||
super("dragon_fireball", EnumAction.NONE, false);
|
||||
addProperties(ACCELERATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesSpellRequirePacket(){
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -33,15 +31,21 @@ public class Fireball extends Spell {
|
||||
Vec3d look = caster.getLookVec();
|
||||
|
||||
if(!world.isRemote){
|
||||
EntitySmallFireball fireball = new EntitySmallFireball(world, caster, 1, 1, 1);
|
||||
|
||||
EntityDragonFireball fireball = new EntityDragonFireball(world, caster, 1, 1, 1);
|
||||
|
||||
fireball.setPosition(caster.posX + look.x, caster.posY + look.y + 1.3, caster.posZ + look.z);
|
||||
fireball.accelerationX = look.x * 0.1;
|
||||
fireball.accelerationY = look.y * 0.1;
|
||||
fireball.accelerationZ = look.z * 0.1;
|
||||
|
||||
double acceleration = getProperty(ACCELERATION).doubleValue() * modifiers.get(WizardryItems.range_upgrade);
|
||||
|
||||
fireball.accelerationX = look.x * acceleration;
|
||||
fireball.accelerationY = look.y * acceleration;
|
||||
fireball.accelerationZ = look.z * acceleration;
|
||||
|
||||
world.spawnEntity(fireball);
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
caster.swingArm(hand);
|
||||
return true;
|
||||
}
|
||||
@@ -54,23 +58,25 @@ public class Fireball extends Spell {
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
EntitySmallFireball fireball = new EntitySmallFireball(world, caster, 1, 1, 1);
|
||||
EntityDragonFireball fireball = new EntityDragonFireball(world, caster, 1, 1, 1);
|
||||
|
||||
double dx = target.posX - caster.posX;
|
||||
double dy = target.getEntityBoundingBox().minY + (double)(target.height / 2.0F)
|
||||
- (caster.posY + (double)(caster.height / 2.0F));
|
||||
double dz = target.posZ - caster.posZ;
|
||||
|
||||
fireball.accelerationX = dx / caster.getDistance(target) * 0.1;
|
||||
fireball.accelerationY = dy / caster.getDistance(target) * 0.1;
|
||||
fireball.accelerationZ = dz / caster.getDistance(target) * 0.1;
|
||||
double acceleration = getProperty(ACCELERATION).doubleValue();
|
||||
|
||||
fireball.accelerationX = dx / caster.getDistance(target) * acceleration;
|
||||
fireball.accelerationY = dy / caster.getDistance(target) * acceleration;
|
||||
fireball.accelerationZ = dz / caster.getDistance(target) * acceleration;
|
||||
|
||||
fireball.setPosition(caster.posX, caster.posY + caster.getEyeHeight(), caster.posZ);
|
||||
|
||||
world.spawnEntity(fireball);
|
||||
}
|
||||
|
||||
caster.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
caster.swingArm(hand);
|
||||
return true;
|
||||
}
|
||||
@@ -1,41 +1,43 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.construct.EntityEarthquake;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.world.World;
|
||||
import electroblob.wizardry.entity.construct.EntityEarthquake;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Earthquake extends SpellConstruct<EntityEarthquake> {
|
||||
public class Earthquake extends SpellConstruct<EntityEarthquake> {
|
||||
|
||||
public static final String SPREAD_SPEED = "spread_speed";
|
||||
|
||||
public Earthquake(){
|
||||
super("earthquake", Tier.MASTER, Element.EARTH, SpellType.ATTACK, 75, 250, EnumAction.NONE, EntityEarthquake::new, -1, WizardrySounds.SPELL_EARTHQUAKE);
|
||||
super("earthquake", EnumAction.NONE, EntityEarthquake::new, true);
|
||||
this.soundValues(2, 1, 0);
|
||||
this.overlap(true);
|
||||
this.floor(true);
|
||||
addProperties(EFFECT_RADIUS, SPREAD_SPEED);
|
||||
}
|
||||
|
||||
// This one spawns particles
|
||||
@Override public boolean doesSpellRequirePacket(){ return true; }
|
||||
@Override public boolean requiresPacket(){ return true; }
|
||||
|
||||
@Override
|
||||
protected void addConstructExtras(EntityEarthquake construct, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
construct.lifetime = (int)(20 * modifiers.get(WizardryItems.blast_upgrade));
|
||||
protected void addConstructExtras(EntityEarthquake construct, EnumFacing side, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
// Calculates the lifetime based on the base radius and spread speed
|
||||
// Also overwrites the -1 lifetime set due to permanent being true
|
||||
construct.lifetime = (int)(getProperty(EFFECT_RADIUS).floatValue()/getProperty(SPREAD_SPEED).floatValue()
|
||||
* modifiers.get(WizardryItems.blast_upgrade));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean spawnConstruct(World world, double x, double y, double z, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
protected boolean spawnConstruct(World world, double x, double y, double z, EnumFacing side, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
|
||||
// TODO: Couldn't this be moved to EntityEarthquake?
|
||||
if(world.isRemote){
|
||||
|
||||
world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, caster.posX,
|
||||
@@ -49,15 +51,13 @@ public class Earthquake extends SpellConstruct<EntityEarthquake> {
|
||||
particleZ = caster.posZ - 1.0d + 2 * world.rand.nextDouble();
|
||||
|
||||
IBlockState block = WizardryUtilities.getBlockEntityIsStandingOn(caster);
|
||||
if(block != null){
|
||||
world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, caster.getEntityBoundingBox().minY,
|
||||
particleZ, particleX - caster.posX, 0, particleZ - caster.posZ,
|
||||
Block.getStateId(block));
|
||||
}
|
||||
world.spawnParticle(EnumParticleTypes.BLOCK_DUST, particleX, caster.getEntityBoundingBox().minY,
|
||||
particleZ, particleX - caster.posX, 0, particleZ - caster.posZ,
|
||||
Block.getStateId(block));
|
||||
}
|
||||
}
|
||||
|
||||
return super.spawnConstruct(world, x, y, z, caster, modifiers);
|
||||
return super.spawnConstruct(world, x, y, z, side, caster, modifiers);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,81 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.AllyDesignationSystem;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventPriority;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class EmpoweringPresence extends Spell {
|
||||
|
||||
public EmpoweringPresence(){
|
||||
super("empowering_presence", EnumAction.BOW, false);
|
||||
addProperties(EFFECT_RADIUS, EFFECT_DURATION, EFFECT_STRENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
List<EntityPlayer> targets = WizardryUtilities.getEntitiesWithinRadius(getProperty(EFFECT_RADIUS).doubleValue()
|
||||
* modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world, EntityPlayer.class);
|
||||
|
||||
for(EntityPlayer target : targets){
|
||||
if(AllyDesignationSystem.isPlayerAlly(caster, target) || target == caster){
|
||||
|
||||
int bonusAmplifier = SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
target.addPotionEffect(new PotionEffect(WizardryPotions.empowerment,
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)),
|
||||
getProperty(EFFECT_STRENGTH).intValue() + bonusAmplifier));
|
||||
}
|
||||
}
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
for(int i = 0; i < 50 * modifiers.get(WizardryItems.blast_upgrade); i++){
|
||||
|
||||
double radius = (1 + world.rand.nextDouble() * 4) * modifiers.get(WizardryItems.blast_upgrade);
|
||||
float angle = world.rand.nextFloat() * (float)Math.PI * 2;
|
||||
|
||||
double x = caster.posX + radius * MathHelper.cos(angle);
|
||||
double y = caster.getEntityBoundingBox().minY;
|
||||
double z = caster.posZ + radius * MathHelper.sin(angle);
|
||||
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.03, 0).time(50).clr(0.5f, 0.4f, 0.75f).spawn(world);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1, 1 + 0.2f * world.rand.nextFloat());
|
||||
return true;
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOW) // Doesn't really matter but there's no point processing it if casting is blocked
|
||||
public static void onSpellCastPreEvent(SpellCastEvent.Pre event){
|
||||
// Empowerment stacks extra potency on top of the existing potency.
|
||||
if(event.getCaster() != null && event.getCaster().isPotionActive(WizardryPotions.empowerment)){
|
||||
|
||||
float potency = 1 + Constants.EMPOWERMENT_POTENCY_PER_LEVEL
|
||||
* (event.getCaster().getActivePotionEffect(WizardryPotions.empowerment).getAmplifier() + 1);
|
||||
|
||||
event.getModifiers().set(SpellModifiers.POTENCY,
|
||||
event.getModifiers().get(SpellModifiers.POTENCY) * potency, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.construct.EntityBubble;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
@@ -13,21 +10,25 @@ import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Entrapment extends SpellRay {
|
||||
|
||||
public static final String DAMAGE_INTERVAL = "damage_interval";
|
||||
|
||||
public Entrapment(){
|
||||
super("entrapment", Tier.ADVANCED, Element.NECROMANCY, SpellType.ATTACK, 35, 75, false, 10, SoundEvents.ENTITY_WITHER_SHOOT);
|
||||
super("entrapment", false, EnumAction.NONE);
|
||||
this.soundValues(1, 0.85f, 0.3f);
|
||||
addProperties(EFFECT_DURATION, DAMAGE_INTERVAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
@@ -38,7 +39,7 @@ public class Entrapment extends SpellRay {
|
||||
EntityBubble bubble = new EntityBubble(world);
|
||||
bubble.setPosition(target.posX, target.posY, target.posZ);
|
||||
bubble.setCaster(caster);
|
||||
bubble.lifetime = ((int)(200 * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
bubble.lifetime = ((int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
bubble.isDarkOrb = true;
|
||||
bubble.damageMultiplier = modifiers.get(SpellModifiers.POTENCY);
|
||||
|
||||
@@ -51,12 +52,12 @@ public class Entrapment extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
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, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Evade extends Spell {
|
||||
|
||||
private static final String EVADE_VELOCITY = "evade_velocity";
|
||||
|
||||
private static final float UPWARD_VELOCITY = 0.25f;
|
||||
|
||||
public Evade(){
|
||||
super("evade", EnumAction.NONE, false);
|
||||
addProperties(EVADE_VELOCITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
Vec3d look = caster.getLookVec();
|
||||
// We want a horizontal only vector
|
||||
look = look.subtract(0, look.y, 0).normalize();
|
||||
|
||||
Vec3d evadeDirection;
|
||||
if(caster.moveStrafing == 0){
|
||||
// If the caster isn't strafing, pick a random direction
|
||||
evadeDirection = look.rotateYaw(world.rand.nextBoolean() ? (float)Math.PI/2f : (float)-Math.PI/2f);
|
||||
}else{
|
||||
// Otherwise, evade always moves whichever direction the caster was already strafing
|
||||
evadeDirection = look.rotateYaw(Math.signum(caster.moveStrafing) * (float)Math.PI/2f);
|
||||
}
|
||||
|
||||
evadeDirection = evadeDirection.scale(getProperty(EVADE_VELOCITY).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
caster.addVelocity(evadeDirection.x, UPWARD_VELOCITY, evadeDirection.z);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class FireBreath extends SpellRay {
|
||||
|
||||
public FireBreath(){
|
||||
super("fire_breath", true, EnumAction.NONE);
|
||||
this.particleVelocity(1);
|
||||
this.particleJitter(0.3);
|
||||
this.particleSpacing(0.25);
|
||||
addProperties(DAMAGE, BURN_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent[] createSounds(){
|
||||
return this.createContinuousSpellSounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, EntityLivingBase entity, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, entity, ticksInUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, double x, double y, double z, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, x, y, z, ticksInUse, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
// Fire can damage armour stands
|
||||
if(target instanceof EntityLivingBase){
|
||||
|
||||
if(MagicDamage.isEntityImmune(DamageType.FIRE, target)){
|
||||
if(!world.isRemote && ticksInUse == 1 && caster instanceof EntityPlayer) ((EntityPlayer)caster)
|
||||
.sendStatusMessage(new TextComponentTranslation("spell.resist", target.getName(),
|
||||
this.getNameForTranslationFormatted()), true);
|
||||
// This now only damages in line with the maxHurtResistantTime. Some mods don't play nicely and fiddle
|
||||
// with this mechanic for their own purposes, so this line makes sure that doesn't affect wizardry.
|
||||
}else if(ticksInUse % ((EntityLivingBase)target).maxHurtResistantTime == 1){
|
||||
target.setFire((int)(getProperty(BURN_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
WizardryUtilities.attackEntityWithoutKnockback(target,
|
||||
MagicDamage.causeDirectMagicDamage(caster, DamageType.FIRE),
|
||||
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(!WizardryUtilities.canDamageBlocks(caster, world)) return false;
|
||||
|
||||
pos = pos.offset(side);
|
||||
|
||||
if(world.isAirBlock(pos)){
|
||||
if(!world.isRemote) world.setBlockState(pos, Blocks.FIRE.getDefaultState());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void spawnParticle(World world, double x, double y, double z, double vx, double vy, double vz){
|
||||
ParticleBuilder.create(Type.MAGIC_FIRE).pos(x, y, z).vel(vx, vy, vz).scale(2 + world.rand.nextFloat()).collide(true).spawn(world);
|
||||
ParticleBuilder.create(Type.MAGIC_FIRE).pos(x, y, z).vel(vx, vy, vz).scale(2 + world.rand.nextFloat()).collide(true).spawn(world);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,110 +0,0 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Firestorm extends SpellRay {
|
||||
|
||||
private static final float BASE_DAMAGE = 6;
|
||||
/** The base duration for which entities are set on fire by this spell. */
|
||||
private static final int BASE_DURATION = 10;
|
||||
|
||||
public Firestorm(){
|
||||
super("firestorm", Tier.MASTER, Element.FIRE, SpellType.ATTACK, 15, 0, true, 10, null);
|
||||
this.particleVelocity(1);
|
||||
this.particleDither(0.3);
|
||||
this.particleSpacing(0.25);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
// Temporary solution until I implement a better continuous sound system
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, modifiers);
|
||||
if(flag){
|
||||
if(ticksInUse % 16 == 0){
|
||||
if(ticksInUse == 0) WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_FIRE, 0.5f, 1.0f);
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, target, modifiers);
|
||||
if(flag){
|
||||
if(ticksInUse % 16 == 0){
|
||||
if(ticksInUse == 0) caster.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1);
|
||||
caster.playSound(WizardrySounds.SPELL_LOOP_FIRE, 0.5f, 1.0f);
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
// Fire can damage armour stands
|
||||
if(target instanceof EntityLivingBase){
|
||||
|
||||
if(MagicDamage.isEntityImmune(DamageType.FIRE, target)){
|
||||
if(!world.isRemote && ticksInUse == 1 && caster instanceof EntityPlayer) ((EntityPlayer)caster)
|
||||
.sendStatusMessage(new TextComponentTranslation("spell.resist", target.getName(),
|
||||
this.getNameForTranslationFormatted()), true);
|
||||
}else{
|
||||
target.setFire((int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
WizardryUtilities.attackEntityWithoutKnockback(target,
|
||||
MagicDamage.causeDirectMagicDamage(caster, DamageType.FIRE),
|
||||
BASE_DAMAGE * modifiers.get(SpellModifiers.POTENCY));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
pos = pos.offset(side);
|
||||
|
||||
if(world.isAirBlock(pos)){
|
||||
if(!world.isRemote) world.setBlockState(pos, Blocks.FIRE.getDefaultState());
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void spawnParticle(World world, double x, double y, double z, double vx, double vy, double vz){
|
||||
ParticleBuilder.create(Type.MAGIC_FIRE).pos(x, y, z).vel(vx, vy, vz).scale(3 + world.rand.nextFloat()).spawn(world);
|
||||
ParticleBuilder.create(Type.MAGIC_FIRE).pos(x, y, z).vel(vx, vy, vz).scale(3 + world.rand.nextFloat()).spawn(world);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,6 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -12,55 +8,45 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class FlameRay extends SpellRay {
|
||||
|
||||
private static final float BASE_DAMAGE = 3;
|
||||
/** The base duration for which entities are set on fire by this spell. */
|
||||
private static final int BASE_DURATION = 10;
|
||||
|
||||
public FlameRay(){
|
||||
super("flame_ray", Tier.APPRENTICE, Element.FIRE, SpellType.ATTACK, 5, 0, true, 10, null);
|
||||
super("flame_ray", true, EnumAction.NONE);
|
||||
this.particleVelocity(1);
|
||||
this.particleSpacing(0.5);
|
||||
addProperties(DAMAGE, BURN_DURATION);
|
||||
this.soundValues(1.5f, 1, 0);
|
||||
}
|
||||
|
||||
// The following three methods serve as a good example of how to implement continuous spell sounds (hint: it's easy)
|
||||
|
||||
@Override
|
||||
protected SoundEvent[] createSounds(){
|
||||
return this.createContinuousSpellSounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
// TODO: Temporary solution until I implement a better continuous sound system
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, modifiers);
|
||||
if(flag){
|
||||
if(ticksInUse % 16 == 0){
|
||||
if(ticksInUse == 0) WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_FIRE, 0.5f, 1.0f);
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
protected void playSound(World world, EntityLivingBase entity, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, entity, ticksInUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, target, modifiers);
|
||||
if(flag){
|
||||
if(ticksInUse % 16 == 0){
|
||||
if(ticksInUse == 0) caster.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1);
|
||||
caster.playSound(WizardrySounds.SPELL_LOOP_FIRE, 0.5f, 1.0f);
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
protected void playSound(World world, double x, double y, double z, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, x, y, z, ticksInUse, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
// Fire can damage armour stands
|
||||
if(target instanceof EntityLivingBase){
|
||||
|
||||
@@ -68,33 +54,33 @@ public class FlameRay extends SpellRay {
|
||||
if(!world.isRemote && ticksInUse == 1 && caster instanceof EntityPlayer) ((EntityPlayer)caster)
|
||||
.sendStatusMessage(new TextComponentTranslation("spell.resist", target.getName(),
|
||||
this.getNameForTranslationFormatted()), true);
|
||||
}else{
|
||||
target.setFire((int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
// This now only damages in line with the maxHurtResistantTime. Some mods don't play nicely and fiddle
|
||||
// with this mechanic for their own purposes, so this line makes sure that doesn't affect wizardry.
|
||||
}else if(ticksInUse % ((EntityLivingBase)target).maxHurtResistantTime == 1){
|
||||
target.setFire((int)(getProperty(BURN_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
WizardryUtilities.attackEntityWithoutKnockback(target,
|
||||
MagicDamage.causeDirectMagicDamage(caster, DamageType.FIRE),
|
||||
BASE_DAMAGE * modifiers.get(SpellModifiers.POTENCY));
|
||||
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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 onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void spawnParticle(World world, double x, double y, double z, double vx, double vy, double vz){
|
||||
ParticleBuilder.create(Type.MAGIC_FIRE).pos(x, y, z).vel(vx, vy, vz).spawn(world);
|
||||
ParticleBuilder.create(Type.MAGIC_FIRE).pos(x, y, z).vel(vx, vy, vz).spawn(world);
|
||||
ParticleBuilder.create(Type.MAGIC_FIRE).pos(x, y, z).vel(vx, vy, vz).collide(true).spawn(world);
|
||||
ParticleBuilder.create(Type.MAGIC_FIRE).pos(x, y, z).vel(vx, vy, vz).collide(true).spawn(world);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class FlamingAxe extends SpellConjuration {
|
||||
|
||||
public FlamingAxe(){
|
||||
super("flaming_axe", Tier.ADVANCED, Element.FIRE, SpellType.UTILITY, 45, 50, WizardryItems.flaming_axe, SoundEvents.ENTITY_BLAZE_SHOOT);
|
||||
super("flaming_axe", WizardryItems.flaming_axe);
|
||||
addProperties(DAMAGE, BURN_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.registry.WizardryEnchantments;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
@@ -15,16 +11,15 @@ import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.ItemBow;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemSword;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class FlamingWeapon extends Spell {
|
||||
|
||||
public FlamingWeapon(){
|
||||
super("flaming_weapon", Tier.ADVANCED, Element.FIRE, SpellType.UTILITY, 35, 70, EnumAction.BOW, false);
|
||||
super("flaming_weapon", EnumAction.BOW, false);
|
||||
addProperties(EFFECT_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,17 +31,17 @@ public class FlamingWeapon extends Spell {
|
||||
|
||||
for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(caster)){
|
||||
|
||||
if((stack.getItem() instanceof ItemSword || stack.getItem() instanceof ItemBow)
|
||||
if((ImbueWeapon.isSword(stack.getItem()) || ImbueWeapon.isBow(stack.getItem()))
|
||||
&& !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.flaming_weapon)){
|
||||
// The enchantment level as determined by the damage multiplier. The + 0.5f is so that
|
||||
// weird float processing doesn't incorrectly round it down.
|
||||
stack.addEnchantment(WizardryEnchantments.flaming_weapon,
|
||||
modifiers.get(SpellModifiers.POTENCY) == 1.0f ? 1
|
||||
: (int)((modifiers.get(SpellModifiers.POTENCY) - 1.0f)
|
||||
/ Constants.DAMAGE_INCREASE_PER_TIER + 0.5f));
|
||||
/ Constants.POTENCY_INCREASE_PER_TIER + 0.5f));
|
||||
|
||||
WizardData.get(caster).setImbuementDuration(WizardryEnchantments.flaming_weapon,
|
||||
(int)(900 * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
|
||||
if(world.isRemote){
|
||||
for(int i=0; i<10; i++){
|
||||
@@ -57,7 +52,7 @@ public class FlamingWeapon extends Spell {
|
||||
}
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,40 +1,47 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Flight extends Spell {
|
||||
|
||||
public static final String SPEED = "speed";
|
||||
public static final String ACCELERATION = "acceleration";
|
||||
|
||||
private static final double Y_NUDGE_ACCELERATION = 0.075;
|
||||
|
||||
public Flight(){
|
||||
super("flight", Tier.MASTER, Element.EARTH, SpellType.UTILITY, 10, 0, EnumAction.NONE, true);
|
||||
super("flight", EnumAction.NONE, true);
|
||||
addProperties(SPEED, ACCELERATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(!caster.isInWater() && !caster.isElytraFlying()){
|
||||
if(!caster.isInWater() && !caster.isInLava() && !caster.isElytraFlying()){
|
||||
|
||||
float speed = getProperty(SPEED).floatValue() * modifiers.get(SpellModifiers.POTENCY);
|
||||
float acceleration = getProperty(ACCELERATION).floatValue() * modifiers.get(SpellModifiers.POTENCY);
|
||||
|
||||
// The division thingy checks if the look direction is the opposite way to the velocity. If this is the
|
||||
// case then the velocity should be added regardless of the player's current speed.
|
||||
if((Math.abs(caster.motionX) < 0.6 || caster.motionX / caster.getLookVec().x < 0)
|
||||
&& (Math.abs(caster.motionZ) < 0.6 || caster.motionZ / caster.getLookVec().z < 0)){
|
||||
caster.addVelocity(caster.getLookVec().x / 20, 0, caster.getLookVec().z / 20);
|
||||
if((Math.abs(caster.motionX) < speed || caster.motionX / caster.getLookVec().x < 0)
|
||||
&& (Math.abs(caster.motionZ) < speed || caster.motionZ / caster.getLookVec().z < 0)){
|
||||
caster.addVelocity(caster.getLookVec().x * acceleration, 0, caster.getLookVec().z * acceleration);
|
||||
}
|
||||
// y velocity is handled separately to stop the player from falling from the sky when they reach maximum
|
||||
// horizontal speed.
|
||||
if(Math.abs(caster.motionY) < 0.6 || caster.motionY / caster.getLookVec().y < 0){
|
||||
caster.motionY += caster.getLookVec().y / 20 + 0.075;
|
||||
if(Math.abs(caster.motionY) < speed || caster.motionY / caster.getLookVec().y < 0){
|
||||
caster.motionY += caster.getLookVec().y * acceleration + Y_NUDGE_ACCELERATION;
|
||||
}
|
||||
caster.fallDistance = 0.0f;
|
||||
|
||||
if(!Wizardry.settings.replaceVanillaFallDamage) caster.fallDistance = 0.0f;
|
||||
}
|
||||
|
||||
if(world.isRemote){
|
||||
@@ -45,12 +52,10 @@ public class Flight extends Spell {
|
||||
x = caster.posX - 1 + world.rand.nextDouble() * 2;
|
||||
y = caster.getEntityBoundingBox().minY + caster.getEyeHeight() - 0.5 + world.rand.nextDouble();
|
||||
z = caster.posZ - 1 + world.rand.nextDouble() * 2;
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, -0.1, 0).time(15).clr(1, 1, 1).spawn(world);
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, -0.1, 0).time(15).clr(1f, 1f, 1f).spawn(world);
|
||||
}
|
||||
|
||||
if(ticksInUse % 24 == 0){
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERDRAGON_FLAP, 0.5F, 1.0f);
|
||||
}
|
||||
if(ticksInUse % 24 == 0) playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.AllyDesignationSystem;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
@@ -16,50 +12,67 @@ import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventPriority;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class FontOfMana extends Spell {
|
||||
|
||||
public FontOfMana(){
|
||||
super("font_of_mana", Tier.MASTER, Element.HEALING, SpellType.UTILITY, 100, 250, EnumAction.BOW, false);
|
||||
super("font_of_mana", EnumAction.BOW, false);
|
||||
this.soundValues(0.7f, 1.2f, 0.4f);
|
||||
addProperties(EFFECT_RADIUS, EFFECT_DURATION, EFFECT_STRENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
double maxRadius = getProperty(EFFECT_RADIUS).doubleValue();
|
||||
|
||||
List<EntityPlayer> targets = WizardryUtilities.getEntitiesWithinRadius(
|
||||
5 * modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world,
|
||||
EntityPlayer.class);
|
||||
maxRadius * modifiers.get(WizardryItems.blast_upgrade),
|
||||
caster.posX, caster.posY, caster.posZ, world, EntityPlayer.class);
|
||||
|
||||
for(EntityPlayer target : targets){
|
||||
if(WizardryUtilities.isPlayerAlly(caster, target) || target == caster){
|
||||
// Damage multiplier can only ever be 1 or 1.6 for master spells, so there's little point in actually
|
||||
// calculating this.
|
||||
if(AllyDesignationSystem.isPlayerAlly(caster, target) || target == caster){
|
||||
target.addPotionEffect(new PotionEffect(WizardryPotions.font_of_mana,
|
||||
(int)(600 * modifiers.get(WizardryItems.duration_upgrade)),
|
||||
modifiers.get(SpellModifiers.POTENCY) > 1 ? 1 : 0));
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)),
|
||||
(int)(getProperty(EFFECT_STRENGTH).intValue() + (modifiers.get(SpellModifiers.POTENCY) - 1) * 2)));
|
||||
}
|
||||
}
|
||||
|
||||
if(world.isRemote){
|
||||
for(int i = 0; i < 100 * modifiers.get(WizardryItems.blast_upgrade); i++){
|
||||
|
||||
double radius = (1 + world.rand.nextDouble() * 4) * modifiers.get(WizardryItems.blast_upgrade);
|
||||
double angle = world.rand.nextDouble() * Math.PI * 2;
|
||||
|
||||
double radius = (1 + world.rand.nextDouble() * (maxRadius - 1)) * modifiers.get(WizardryItems.blast_upgrade);
|
||||
float angle = world.rand.nextFloat() * (float)Math.PI * 2;
|
||||
;
|
||||
float hue = world.rand.nextFloat() * 0.4f;
|
||||
|
||||
double x = caster.posX + radius * Math.cos(angle);
|
||||
|
||||
double x = caster.posX + radius * MathHelper.cos(angle);
|
||||
double y = caster.getEntityBoundingBox().minY;
|
||||
double z = caster.posZ + radius * Math.sin(angle);
|
||||
|
||||
double z = caster.posZ + radius * MathHelper.sin(angle);
|
||||
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.03, 0).time(50)
|
||||
.clr(1, 1 - hue, 0.6f + hue).spawn(world);
|
||||
.clr(1, 1 - hue, 0.6f + hue).spawn(world);
|
||||
}
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F,
|
||||
world.rand.nextFloat() * 0.4F + 1.0F);
|
||||
playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@SubscribeEvent(priority = EventPriority.LOW) // Doesn't really matter but there's no point processing it if casting is blocked
|
||||
public static void onSpellCastPreEvent(SpellCastEvent.Pre event){
|
||||
// Moved from ItemWand (quite why this wasn't done with modifiers before I don't know!)
|
||||
if(event.getCaster() != null && event.getCaster().isPotionActive(WizardryPotions.font_of_mana)){
|
||||
// Dividing by this rather than setting it takes upgrades and font of mana into account simultaneously
|
||||
event.getModifiers().set(WizardryItems.cooldown_upgrade, event.getModifiers().get(WizardryItems.cooldown_upgrade)
|
||||
/ (2 + event.getCaster().getActivePotionEffect(WizardryPotions.font_of_mana).getAmplifier()), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.entity.projectile.EntityForceArrow;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ForceArrow extends SpellArrow<EntityForceArrow> {
|
||||
|
||||
public ForceArrow(){
|
||||
super("force_arrow", EntityForceArrow::new);
|
||||
this.addProperties(Spell.DAMAGE);
|
||||
this.soundValues(1, 1.3f, 0.2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addArrowExtras(EntityForceArrow arrow, @Nullable EntityLivingBase caster, SpellModifiers modifiers){
|
||||
arrow.setMana((int)(this.getCost() * modifiers.get(SpellModifiers.COST)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.entity.construct.EntityForcefield;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
|
||||
public class Forcefield extends SpellConstruct<EntityForcefield> {
|
||||
|
||||
public Forcefield(){
|
||||
super("forcefield", EnumAction.BOW, EntityForcefield::new, false);
|
||||
addProperties(Spell.EFFECT_RADIUS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConstructExtras(EntityForcefield construct, EnumFacing side, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
construct.setRadius(getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.block.BlockThorns;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.tileentity.TileEntityPlayerSaveTimed;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class ForestOfThorns extends Spell {
|
||||
|
||||
public ForestOfThorns(){
|
||||
super("forest_of_thorns", EnumAction.BOW, false);
|
||||
addProperties(EFFECT_RADIUS, DURATION, DAMAGE);
|
||||
}
|
||||
|
||||
@Override public boolean requiresPacket(){ return false; }
|
||||
@Override public boolean canBeCastByNPCs(){ return true; }
|
||||
@Override public boolean canBeCastByDispensers(){ return true; }
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
if(!summonThorns(world, caster, caster.getPosition(), modifiers)) return false;
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){
|
||||
if(!summonThorns(world, caster, caster.getPosition(), modifiers)) return false;
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, double x, double y, double z, EnumFacing direction, int ticksInUse, int duration, SpellModifiers modifiers){
|
||||
if(!summonThorns(world, null, new BlockPos(x, y, z).offset(direction), modifiers)) return false;
|
||||
this.playSound(world, x, y, z, ticksInUse, duration, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean summonThorns(World world, @Nullable EntityLivingBase caster, BlockPos origin, SpellModifiers modifiers){
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
double radius = getProperty(EFFECT_RADIUS).doubleValue() * modifiers.get(WizardryItems.blast_upgrade);
|
||||
|
||||
List<BlockPos> ring = new ArrayList<>((int)(7 * radius)); // 7 is a bit more than 2 pi
|
||||
|
||||
for(int x = -(int)radius; x <= radius; x++){
|
||||
|
||||
for(int z = -(int)radius; z <= radius; z++){
|
||||
|
||||
double distance = MathHelper.sqrt(x*x + z*z);
|
||||
|
||||
if(distance > radius || distance < radius - 1.5) continue;
|
||||
|
||||
Integer y = WizardryUtilities.getNearestSurface(world, origin.add(x, 0, z), EnumFacing.UP, (int)radius, true, WizardryUtilities.SurfaceCriteria.BUILDABLE);
|
||||
if(y != null) ring.add(new BlockPos(origin.getX() + x, y, origin.getZ() + z));
|
||||
}
|
||||
}
|
||||
|
||||
if(ring.isEmpty()) return false;
|
||||
|
||||
// Because we're always using EnumFacing.UP in the code above, we can be sure that pos is the block above the floor
|
||||
for(BlockPos pos : ring){
|
||||
|
||||
((BlockThorns)WizardryBlocks.thorns).placeAt(world, pos, 3);
|
||||
|
||||
for(int i=0; i<2; i++){
|
||||
|
||||
TileEntity tileentity = world.getTileEntity(pos.up(i));
|
||||
|
||||
if(tileentity instanceof TileEntityPlayerSaveTimed){
|
||||
((TileEntityPlayerSaveTimed)tileentity).setLifetime((int)(getProperty(DURATION).floatValue()
|
||||
* modifiers.get(WizardryItems.duration_upgrade)));
|
||||
if(caster != null) ((TileEntityPlayerSaveTimed)tileentity).setCaster(caster);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
@@ -12,19 +9,16 @@ import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class ForestsCurse extends SpellAreaEffect {
|
||||
|
||||
private static final int BASE_DAMAGE = 4;
|
||||
private static final int BASE_DURATION = 140;
|
||||
|
||||
public ForestsCurse(){
|
||||
super("forests_curse", Tier.MASTER, Element.EARTH, SpellType.ATTACK, 75, 200, EnumAction.BOW, 5, SoundEvents.ENTITY_WITHER_SPAWN);
|
||||
super("forests_curse", EnumAction.BOW);
|
||||
this.soundValues(1, 1.1f, 0.2f);
|
||||
addProperties(DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -33,13 +27,15 @@ public class ForestsCurse extends SpellAreaEffect {
|
||||
if(!MagicDamage.isEntityImmune(DamageType.POISON, target) && WizardryUtilities.isLiving(target)){
|
||||
|
||||
target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.POISON),
|
||||
BASE_DAMAGE * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
int duration = (int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade));
|
||||
|
||||
target.addPotionEffect(new PotionEffect(MobEffects.POISON, duration, 2));
|
||||
target.addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, duration, 2));
|
||||
target.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, duration, 2));
|
||||
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
int bonusAmplifier = SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY));
|
||||
int duration = (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade));
|
||||
int amplifier = (int)(getProperty(EFFECT_STRENGTH).floatValue() + bonusAmplifier);
|
||||
|
||||
target.addPotionEffect(new PotionEffect(MobEffects.POISON, duration, amplifier));
|
||||
target.addPotionEffect(new PotionEffect(MobEffects.SLOWNESS, duration, amplifier));
|
||||
target.addPotionEffect(new PotionEffect(MobEffects.WEAKNESS, duration, amplifier));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.living.EntityBlazeMinion;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -17,33 +12,33 @@ import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.monster.EntityBlaze;
|
||||
import net.minecraft.entity.monster.EntityMagmaCube;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
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;
|
||||
|
||||
public class Freeze extends SpellRay {
|
||||
|
||||
/** The base duration of the frostbite effect appled by this spell. */
|
||||
private static final int BASE_DURATION = 200;
|
||||
/** The base damage dealt to entities vulnerable to frost. */
|
||||
private static final float BASE_DAMAGE = 3;
|
||||
|
||||
public Freeze(){
|
||||
super("freeze", Tier.BASIC, Element.ICE, SpellType.ATTACK, 5, 10, false, 10, WizardrySounds.SPELL_ICE);
|
||||
super("freeze", false, EnumAction.NONE);
|
||||
this.soundValues(1, 1.4f, 0.4f);
|
||||
addProperties(DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH);
|
||||
this.hitLiquids(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
if(target instanceof EntityBlaze || target instanceof EntityMagmaCube || target instanceof EntityBlazeMinion){
|
||||
if(target instanceof EntityBlaze || target instanceof EntityMagmaCube){
|
||||
target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.FROST),
|
||||
BASE_DAMAGE * modifiers.get(SpellModifiers.POTENCY));
|
||||
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
}
|
||||
|
||||
if(MagicDamage.isEntityImmune(DamageType.FROST, target)){
|
||||
@@ -51,11 +46,12 @@ public class Freeze extends SpellRay {
|
||||
new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted()), true);
|
||||
}else{
|
||||
((EntityLivingBase)target).addPotionEffect(new PotionEffect(WizardryPotions.frost,
|
||||
(int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)), 1));
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)),
|
||||
getProperty(EFFECT_STRENGTH).intValue()));
|
||||
}
|
||||
|
||||
if(target.isBurning()) target.extinguish();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -63,24 +59,27 @@ public class Freeze extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(world.getBlockState(pos).getBlock() == Blocks.WATER && !world.isRemote){
|
||||
world.setBlockState(pos, Blocks.ICE.getDefaultState());
|
||||
}else if(world.getBlockState(pos).getBlock() == Blocks.LAVA && !world.isRemote){
|
||||
world.setBlockState(pos, Blocks.OBSIDIAN.getDefaultState());
|
||||
}else if(world.getBlockState(pos).getBlock() == Blocks.FLOWING_LAVA && !world.isRemote){
|
||||
world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState());
|
||||
}else if(side == EnumFacing.UP && !world.isRemote && world.isSideSolid(pos, EnumFacing.UP)
|
||||
&& WizardryUtilities.canBlockBeReplaced(world, pos.up())){
|
||||
world.setBlockState(pos.up(), Blocks.SNOW_LAYER.getDefaultState());
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.canDamageBlocks(caster, world)){
|
||||
|
||||
if(world.getBlockState(pos).getBlock() == Blocks.WATER && !world.isRemote){
|
||||
world.setBlockState(pos, Blocks.ICE.getDefaultState());
|
||||
}else if(world.getBlockState(pos).getBlock() == Blocks.LAVA && !world.isRemote){
|
||||
world.setBlockState(pos, Blocks.OBSIDIAN.getDefaultState());
|
||||
}else if(world.getBlockState(pos).getBlock() == Blocks.FLOWING_LAVA && !world.isRemote){
|
||||
world.setBlockState(pos, Blocks.COBBLESTONE.getDefaultState());
|
||||
}else if(side == EnumFacing.UP && !world.isRemote && world.isSideSolid(pos, EnumFacing.UP)
|
||||
&& WizardryUtilities.canBlockBeReplaced(world, pos.up())){
|
||||
world.setBlockState(pos.up(), Blocks.SNOW_LAYER.getDefaultState());
|
||||
}
|
||||
}
|
||||
|
||||
return true; // Always succeeds if it hits a block
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.registry.WizardryEnchantments;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
@@ -15,9 +11,7 @@ import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.ItemBow;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemSword;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
@@ -30,7 +24,8 @@ public class FreezingWeapon extends Spell {
|
||||
public static final String FREEZING_ARROW_NBT_KEY = "frostLevel";
|
||||
|
||||
public FreezingWeapon(){
|
||||
super("freezing_weapon", Tier.ADVANCED, Element.ICE, SpellType.UTILITY, 35, 70, EnumAction.BOW, false);
|
||||
super("freezing_weapon", EnumAction.BOW, false);
|
||||
addProperties(EFFECT_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -42,17 +37,17 @@ public class FreezingWeapon extends Spell {
|
||||
|
||||
for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(caster)){
|
||||
|
||||
if((stack.getItem() instanceof ItemSword || stack.getItem() instanceof ItemBow)
|
||||
if((ImbueWeapon.isSword(stack.getItem()) || ImbueWeapon.isBow(stack.getItem()))
|
||||
&& !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.freezing_weapon)){
|
||||
// The enchantment level as determined by the damage multiplier. The + 0.5f is so that
|
||||
// weird float processing doesn't incorrectly round it down.
|
||||
stack.addEnchantment(WizardryEnchantments.freezing_weapon,
|
||||
modifiers.get(SpellModifiers.POTENCY) == 1.0f ? 1
|
||||
: (int)((modifiers.get(SpellModifiers.POTENCY) - 1.0f)
|
||||
/ Constants.DAMAGE_INCREASE_PER_TIER + 0.5f));
|
||||
/ Constants.POTENCY_INCREASE_PER_TIER + 0.5f));
|
||||
|
||||
WizardData.get(caster).setImbuementDuration(WizardryEnchantments.freezing_weapon,
|
||||
(int)(900 * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
|
||||
if(world.isRemote){
|
||||
for(int i=0; i<10; i++){
|
||||
@@ -63,7 +58,7 @@ public class FreezingWeapon extends Spell {
|
||||
}
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1, 1);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
@@ -14,7 +10,7 @@ import net.minecraft.world.World;
|
||||
public class FrostAxe extends SpellConjuration {
|
||||
|
||||
public FrostAxe(){
|
||||
super("frost_axe", Tier.ADVANCED, Element.ICE, SpellType.UTILITY, 45, 50, WizardryItems.frost_axe, WizardrySounds.SPELL_ICE);
|
||||
super("frost_axe", WizardryItems.frost_axe);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -13,57 +9,45 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.monster.EntityBlaze;
|
||||
import net.minecraft.entity.monster.EntityMagmaCube;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class FrostRay extends SpellRay {
|
||||
|
||||
private static final float BASE_DAMAGE = 3;
|
||||
/** The base duration of the forstbite effect applied by this spell. */
|
||||
private static final int BASE_DURATION = 200;
|
||||
|
||||
public FrostRay(){
|
||||
super("frost_ray", Tier.APPRENTICE, Element.ICE, SpellType.ATTACK, 5, 0, true, 10, null);
|
||||
super("frost_ray", true, EnumAction.NONE);
|
||||
this.particleVelocity(1);
|
||||
this.particleSpacing(0.5);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
// TODO: Temporary solution until I implement a better continuous sound system
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, modifiers);
|
||||
if(flag){
|
||||
if(ticksInUse % 12 == 0){
|
||||
if(ticksInUse == 0) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.5f, 1);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_ICE, 0.5f, 1);
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
addProperties(DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, target, modifiers);
|
||||
if(flag){
|
||||
if(ticksInUse % 12 == 0){
|
||||
if(ticksInUse == 0) caster.playSound(WizardrySounds.SPELL_ICE, 0.5f, 1);
|
||||
caster.playSound(WizardrySounds.SPELL_LOOP_ICE, 0.5f, 1);
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
protected SoundEvent[] createSounds(){
|
||||
return this.createContinuousSpellSounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, EntityLivingBase entity, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, entity, ticksInUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, double x, double y, double z, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, x, y, z, ticksInUse, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
@@ -73,32 +57,33 @@ public class FrostRay extends SpellRay {
|
||||
if(!world.isRemote && ticksInUse == 1 && caster instanceof EntityPlayer) ((EntityPlayer)caster)
|
||||
.sendStatusMessage(new TextComponentTranslation("spell.resist", target.getName(),
|
||||
this.getNameForTranslationFormatted()), true);
|
||||
}else{
|
||||
// This now only damages in line with the maxHurtResistantTime. Some mods don't play nicely and fiddle
|
||||
// with this mechanic for their own purposes, so this line makes sure that doesn't affect wizardry.
|
||||
}else if(ticksInUse % ((EntityLivingBase)target).maxHurtResistantTime == 1){
|
||||
// For frost ray the entity can move slightly, unlike freeze
|
||||
((EntityLivingBase)target).addPotionEffect(new PotionEffect(WizardryPotions.frost,
|
||||
(int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)), 0));
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)),
|
||||
getProperty(EFFECT_STRENGTH).intValue()));
|
||||
|
||||
float damage = BASE_DAMAGE * modifiers.get(SpellModifiers.POTENCY);
|
||||
float damage = getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY);
|
||||
if(target instanceof EntityBlaze || target instanceof EntityMagmaCube) damage *= 2;
|
||||
|
||||
WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster,
|
||||
DamageType.FROST), damage);
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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 onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -106,8 +91,8 @@ public class FrostRay extends SpellRay {
|
||||
protected void spawnParticle(World world, double x, double y, double z, double vx, double vy, double vz){
|
||||
float brightness = world.rand.nextFloat();
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(vx, vy, vz).time(8 + world.rand.nextInt(12))
|
||||
.clr(0.4f + 0.6f * brightness, 0.6f + 0.4f*brightness, 1).spawn(world);
|
||||
ParticleBuilder.create(Type.SNOW).pos(x, y, z).vel(vx, vy, vz).time(8 + world.rand.nextInt(12)).spawn(world);
|
||||
.clr(0.4f + 0.6f * brightness, 0.6f + 0.4f*brightness, 1).collide(true).spawn(world);
|
||||
ParticleBuilder.create(Type.SNOW).pos(x, y, z).vel(vx, vy, vz).time(8 + world.rand.nextInt(12)).collide(true).spawn(world);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,41 +1,66 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Glide extends Spell {
|
||||
|
||||
public static final String SPEED = "speed";
|
||||
public static final String FALL_SPEED = "fall_speed";
|
||||
public static final String ACCELERATION = "acceleration";
|
||||
|
||||
public Glide(){
|
||||
super("glide", Tier.ADVANCED, Element.EARTH, SpellType.UTILITY, 5, 0, EnumAction.NONE, true);
|
||||
super("glide", EnumAction.NONE, true);
|
||||
addProperties(SPEED, FALL_SPEED, ACCELERATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, EntityLivingBase entity, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
if(ticksInUse == 0 && world.isRemote) Wizardry.proxy.playSpellSoundLoop(entity, this, this.sounds[0], this.sounds[0], this.sounds[0],
|
||||
WizardrySounds.SPELLS, volume, pitch + pitchVariation * (world.rand.nextFloat() - 0.5f));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, double x, double y, double z, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
if(ticksInUse == 0 && world.isRemote){
|
||||
Wizardry.proxy.playSpellSoundLoop(world, x, y, z, this, this.sounds[0], this.sounds[0], this.sounds[0],
|
||||
WizardrySounds.SPELLS, volume, pitch + pitchVariation * (world.rand.nextFloat() - 0.5f), duration);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(caster.motionY < -0.1 && !caster.isInWater()){
|
||||
caster.motionY = -0.1;
|
||||
if(Math.abs(caster.motionX) < 0.4 && Math.abs(caster.motionZ) < 0.4){
|
||||
caster.addVelocity(caster.getLookVec().x / 8, 0, caster.getLookVec().z / 8);
|
||||
// entityplayer.moveEntity(entityplayer.motionX*10, 0, entityplayer.motionZ*10);
|
||||
|
||||
float speed = getProperty(SPEED).floatValue() * modifiers.get(SpellModifiers.POTENCY);
|
||||
// There seems to be some sort of 'terminal velocity', presumably due to the slight slowing-down effect in
|
||||
// vanilla - this means we have to apply potency modifiers to the acceleration as well as the speed or they
|
||||
// appear to have no effect (a bug which had me confused for quite a while!) This also applies to flight.
|
||||
float acceleration = getProperty(ACCELERATION).floatValue() * modifiers.get(SpellModifiers.POTENCY);
|
||||
|
||||
caster.motionY = -getProperty(FALL_SPEED).floatValue();
|
||||
if(Math.abs(caster.motionX) < speed && Math.abs(caster.motionZ) < speed){
|
||||
caster.addVelocity(caster.getLookVec().x * acceleration, 0, caster.getLookVec().z * acceleration);
|
||||
}
|
||||
caster.fallDistance = 0.0f;
|
||||
|
||||
if(!Wizardry.settings.replaceVanillaFallDamage) caster.fallDistance = 0.0f;
|
||||
}
|
||||
|
||||
if(world.isRemote){
|
||||
double x = caster.posX - 0.25 + world.rand.nextDouble() / 2;
|
||||
double y = caster.getEntityBoundingBox().minY + world.rand.nextDouble();
|
||||
double z = caster.posZ - 0.25 + world.rand.nextDouble() / 2;
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, -0.1, 0).time(15).clr(1, 1, 1).spawn(world);
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, -0.1, 0).time(15).clr(1f, 1f, 1f).spawn(world);
|
||||
x = caster.posX - 0.25 + world.rand.nextDouble() / 2;
|
||||
y = caster.getEntityBoundingBox().minY + world.rand.nextDouble();
|
||||
z = caster.posZ - 0.25 + world.rand.nextDouble() / 2;
|
||||
@@ -43,7 +68,7 @@ public class Glide extends Spell {
|
||||
}
|
||||
|
||||
if(ticksInUse % 24 == 0){
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ITEM_ELYTRA_FLYING, 0.5F, 1.0f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -0,0 +1,419 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.data.IVariable;
|
||||
import electroblob.wizardry.data.Persistence;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.RayTracer;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.network.play.server.SPacketEntityVelocity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class Grapple extends Spell {
|
||||
|
||||
/** The speed at which the vine extends/retracts from the caster, in blocks per tick. */
|
||||
public static final String EXTENSION_SPEED = "extension_speed";
|
||||
/** The speed at which the vine reels in the caster or target, in blocks per tick. */
|
||||
public static final String REEL_SPEED = "reel_speed";
|
||||
|
||||
public static final IVariable<RayTraceResult> TARGET_KEY = new IVariable.Variable<RayTraceResult>(Persistence.NEVER)
|
||||
.withTicker(Grapple::update);
|
||||
|
||||
/** The distance from the target position at which the spell will stop reeling in entities. */
|
||||
private static final double MINIMUM_REEL_DISTANCE = 3;
|
||||
/** The acceleration with which the vine reels in the caster or target. */
|
||||
private static final double REEL_ACCELERATION = 0.3;
|
||||
/** The speed at which the caster or target is lowered when the caster is sneaking. */
|
||||
private static final double PAYOUT_SPEED = 0.25;
|
||||
/** Once attached, the vine can stretch beyond the maximum range by this factor before breaking. */
|
||||
private static final double STRETCH_LIMIT = 1.5;
|
||||
/** The distance between spawned particles. */
|
||||
protected static final double PARTICLE_SPACING = 1.5;
|
||||
/** The maximum jitter (random position offset) for spawned particles. */
|
||||
protected static final double PARTICLE_JITTER = 0.04;
|
||||
|
||||
public Grapple(){
|
||||
super("grapple", EnumAction.NONE, true);
|
||||
addProperties(RANGE, EXTENSION_SPEED, REEL_SPEED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByNPCs(){
|
||||
return super.canBeCastByNPCs();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByDispensers(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent[] createSounds(){
|
||||
return this.createSoundsWithSuffixes("shoot", "attach", "pull", "release");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
WizardData data = WizardData.get(caster);
|
||||
|
||||
Vec3d origin = new Vec3d(caster.posX, caster.getEntityBoundingBox().minY + caster.getEyeHeight(), caster.posZ);
|
||||
|
||||
float extensionSpeed = getProperty(EXTENSION_SPEED).floatValue() * modifiers.get(SpellModifiers.POTENCY);
|
||||
|
||||
RayTraceResult hit = data.getVariable(TARGET_KEY);
|
||||
|
||||
// Initial targeting
|
||||
if(hit == null){
|
||||
hit = findTarget(world, caster, origin, caster.getLookVec(), modifiers);
|
||||
data.setVariable(TARGET_KEY, hit);
|
||||
caster.swingArm(hand);
|
||||
// This condition prevents the sound playing every tick after a missed shot has finished extending
|
||||
if(hit.typeOfHit != RayTraceResult.Type.MISS
|
||||
|| ticksInUse * extensionSpeed < getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade)){
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers, "shoot");
|
||||
}
|
||||
}
|
||||
|
||||
Vec3d target = hit.hitVec;
|
||||
|
||||
if(hit.entityHit instanceof EntityLivingBase){
|
||||
// If the target is an entity, we need to use the entity's centre rather than the original hit position
|
||||
// because the entity will have moved!
|
||||
target = new Vec3d(hit.entityHit.posX, hit.entityHit.getEntityBoundingBox().minY + hit.entityHit.height/2, hit.entityHit.posZ);
|
||||
}
|
||||
|
||||
double distance = origin.distanceTo(target);
|
||||
Vec3d direction = target.subtract(origin).normalize();
|
||||
|
||||
double maxLength = getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade) * STRETCH_LIMIT;
|
||||
|
||||
// If the vine stretched too far
|
||||
if(distance > maxLength){
|
||||
if(world.isRemote && (ticksInUse-1) * extensionSpeed < distance){
|
||||
spawnLeafParticles(world, origin.subtract(0, SpellRay.Y_OFFSET, 0), direction, distance);
|
||||
}
|
||||
data.setVariable(TARGET_KEY, null);
|
||||
return false; // The spell is finished
|
||||
}
|
||||
|
||||
boolean extending = ticksInUse * extensionSpeed < distance;
|
||||
|
||||
if(extending){
|
||||
// Extension
|
||||
if(world.isRemote){
|
||||
// world.getTotalWorldTime() - ticksInUse generates a constant but unique seed each time the spell is cast
|
||||
ParticleBuilder.create(Type.VINE).entity(caster).pos(0, caster.getEyeHeight() - SpellRay.Y_OFFSET, 0)
|
||||
.target(origin.add(direction.scale(ticksInUse * extensionSpeed))).tvel(direction.scale(extensionSpeed))
|
||||
.seed(world.getTotalWorldTime() - ticksInUse).spawn(world);
|
||||
}
|
||||
|
||||
}else{
|
||||
// Retraction
|
||||
Vec3d velocity = direction.scale(getProperty(REEL_SPEED).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
int retractTime = ticksInUse - (int)(distance/extensionSpeed);
|
||||
|
||||
switch(hit.typeOfHit){
|
||||
|
||||
case BLOCK:
|
||||
// Payout
|
||||
if(caster.isSneaking() && ItemArtefact.isArtefactActive(caster, WizardryItems.charm_abseiling))
|
||||
velocity = new Vec3d(velocity.x, distance < maxLength-1 ? -PAYOUT_SPEED : distance-maxLength+1, velocity.z);
|
||||
|
||||
// Reel the caster towards the block hit
|
||||
double ax = (velocity.x - caster.motionX) * REEL_ACCELERATION;
|
||||
double ay = (velocity.y - caster.motionY) * REEL_ACCELERATION;
|
||||
double az = (velocity.z - caster.motionZ) * REEL_ACCELERATION;
|
||||
caster.addVelocity(ax, ay, az);
|
||||
|
||||
if(caster.motionY > 0 && !Wizardry.settings.replaceVanillaFallDamage) caster.fallDistance = 0; // Reset fall distance if the caster moves upwards
|
||||
|
||||
if(world.isRemote){
|
||||
ParticleBuilder.create(Type.VINE).entity(caster).pos(0, caster.getEyeHeight() - SpellRay.Y_OFFSET, 0)
|
||||
.target(target).seed(world.getTotalWorldTime() - ticksInUse).spawn(world);
|
||||
}
|
||||
|
||||
if(retractTime == 1){ // Just hit
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers, "pull");
|
||||
this.playSound(world, hit.hitVec, ticksInUse, -1, modifiers, "attach");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ENTITY:
|
||||
// Payout
|
||||
if(caster.isSneaking() && ItemArtefact.isArtefactActive(caster, WizardryItems.charm_abseiling))
|
||||
velocity = new Vec3d(velocity.x, distance < maxLength-1 ? PAYOUT_SPEED : maxLength-1-distance, velocity.z);
|
||||
|
||||
// Reel the entity hit towards the caster
|
||||
Entity entity = hit.entityHit;
|
||||
|
||||
if(distance > MINIMUM_REEL_DISTANCE){
|
||||
double ax1 = (-velocity.x - entity.motionX) * REEL_ACCELERATION;
|
||||
double ay1 = (-velocity.y - entity.motionY) * REEL_ACCELERATION;
|
||||
double az1 = (-velocity.z - entity.motionZ) * REEL_ACCELERATION;
|
||||
entity.addVelocity(ax1, ay1, az1);
|
||||
// Player motion is handled on that player's client so needs packets
|
||||
if(entity instanceof EntityPlayerMP){
|
||||
((EntityPlayerMP)entity).connection.sendPacket(new SPacketEntityVelocity(entity));
|
||||
}
|
||||
}
|
||||
|
||||
if(world.isRemote){
|
||||
ParticleBuilder.create(Type.VINE).entity(caster).pos(0, caster.getEyeHeight() - SpellRay.Y_OFFSET, 0)
|
||||
.target(entity).seed(world.getTotalWorldTime() - ticksInUse).spawn(world);
|
||||
}
|
||||
|
||||
if(retractTime == 1){ // Just hit
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers, "pull");
|
||||
this.playSound(world, entity.posX, entity.posY, entity.posZ, ticksInUse, -1, modifiers, "attach");
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
// Missed
|
||||
if(world.isRemote && (ticksInUse-1) * extensionSpeed < distance){
|
||||
spawnLeafParticles(world, origin.subtract(0, SpellRay.Y_OFFSET, 0), direction, distance);
|
||||
}
|
||||
//caster.resetActiveHand();
|
||||
data.setVariable(TARGET_KEY, null);
|
||||
return false; // The spell is finished
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){
|
||||
|
||||
Vec3d origin = new Vec3d(caster.posX, caster.getEntityBoundingBox().minY + caster.getEyeHeight(), caster.posZ);
|
||||
|
||||
// If the target is an entity, we need to use the entity's centre rather than the original hit position
|
||||
// because the entity will have moved!
|
||||
Vec3d targetVec = new Vec3d(target.posX, target.getEntityBoundingBox().minY + target.height/2, target.posZ);
|
||||
|
||||
double distance = origin.distanceTo(targetVec);
|
||||
|
||||
// Can't cast the spell at all if the target is too far away
|
||||
if(ticksInUse <= 1 && distance > getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade))
|
||||
return false;
|
||||
|
||||
Vec3d vec = targetVec.subtract(origin).normalize();
|
||||
|
||||
float extensionSpeed = getProperty(EXTENSION_SPEED).floatValue() * modifiers.get(SpellModifiers.POTENCY);
|
||||
|
||||
// If the vine stretched too far
|
||||
if(distance > getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade) * STRETCH_LIMIT){
|
||||
if(world.isRemote && (ticksInUse-1) * extensionSpeed < distance){
|
||||
spawnLeafParticles(world, origin.subtract(0, SpellRay.Y_OFFSET, 0), vec, distance);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vec3d hookPosition;
|
||||
|
||||
if(ticksInUse * extensionSpeed < distance){
|
||||
// Extension
|
||||
hookPosition = origin.add(vec.scale(ticksInUse * extensionSpeed));
|
||||
|
||||
}else{
|
||||
// Retraction
|
||||
Vec3d velocity = vec.scale(getProperty(REEL_SPEED).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
// Reel the entity hit towards the caster
|
||||
if(distance > MINIMUM_REEL_DISTANCE){
|
||||
double ax1 = (-velocity.x - target.motionX) * REEL_ACCELERATION;
|
||||
double ay1 = (-velocity.y - target.motionY) * REEL_ACCELERATION;
|
||||
double az1 = (-velocity.z - target.motionZ) * REEL_ACCELERATION;
|
||||
target.addVelocity(ax1, ay1, az1);
|
||||
// Player motion is handled on that player's client so needs packets
|
||||
if(target instanceof EntityPlayerMP){
|
||||
((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target));
|
||||
}
|
||||
}
|
||||
|
||||
hookPosition = targetVec;
|
||||
}
|
||||
|
||||
if(world.isRemote){
|
||||
// world.getTotalWorldTime() - ticksInUse generates a constant but unique seed each time the spell is cast
|
||||
ParticleBuilder.create(Type.VINE).pos(origin).target(hookPosition).tvel(vec.scale(extensionSpeed))
|
||||
.seed(world.getTotalWorldTime() - ticksInUse).spawn(world);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, double x, double y, double z, EnumFacing direction, int ticksInUse, int duration, SpellModifiers modifiers){
|
||||
|
||||
Vec3d origin = new Vec3d(x, y, z);
|
||||
|
||||
RayTraceResult result = findTarget(world, null, origin, new Vec3d(direction.getDirectionVec()), modifiers);
|
||||
|
||||
if(result.entityHit instanceof EntityLivingBase){
|
||||
|
||||
Entity entity = result.entityHit;
|
||||
|
||||
// If the target is an entity, we need to use the entity's centre rather than the original hit position
|
||||
// because the entity will have moved!
|
||||
Vec3d target = new Vec3d(entity.posX, entity.getEntityBoundingBox().minY + entity.height/2, entity.posZ);
|
||||
|
||||
double distance = origin.distanceTo(target);
|
||||
Vec3d vec = target.subtract(origin).normalize();
|
||||
|
||||
float extensionSpeed = getProperty(EXTENSION_SPEED).floatValue() * modifiers.get(SpellModifiers.POTENCY);
|
||||
|
||||
// If the vine stretched too far
|
||||
if(distance > getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade) * STRETCH_LIMIT){
|
||||
if(world.isRemote && (ticksInUse-1) * extensionSpeed < distance){
|
||||
spawnLeafParticles(world, origin.subtract(0, SpellRay.Y_OFFSET, 0), vec, distance);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vec3d hookPosition;
|
||||
|
||||
if(ticksInUse * extensionSpeed < distance){
|
||||
// Extension
|
||||
hookPosition = origin.add(vec.scale(ticksInUse * extensionSpeed));
|
||||
|
||||
}else{
|
||||
// Retraction
|
||||
Vec3d velocity = vec.scale(getProperty(REEL_SPEED).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
// Reel the entity hit towards the caster
|
||||
if(distance > MINIMUM_REEL_DISTANCE){
|
||||
double ax1 = (-velocity.x - entity.motionX) * REEL_ACCELERATION;
|
||||
double ay1 = (-velocity.y - entity.motionY) * REEL_ACCELERATION;
|
||||
double az1 = (-velocity.z - entity.motionZ) * REEL_ACCELERATION;
|
||||
entity.addVelocity(ax1, ay1, az1);
|
||||
// Player motion is handled on that player's client so needs packets
|
||||
if(entity instanceof EntityPlayerMP){
|
||||
((EntityPlayerMP)entity).connection.sendPacket(new SPacketEntityVelocity(entity));
|
||||
}
|
||||
}
|
||||
|
||||
hookPosition = target;
|
||||
}
|
||||
|
||||
if(world.isRemote){
|
||||
// world.getTotalWorldTime() - ticksInUse generates a constant but unique seed each time the spell is cast
|
||||
ParticleBuilder.create(Type.VINE).pos(origin).target(hookPosition).seed(world.getTotalWorldTime() - ticksInUse).spawn(world);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void finishCasting(World world, @Nullable EntityLivingBase caster, double x, double y, double z, EnumFacing facing, int duration, SpellModifiers modifiers){
|
||||
|
||||
Vec3d origin = null;
|
||||
Vec3d direction = null;
|
||||
Vec3d target = null;
|
||||
|
||||
if(caster != null){
|
||||
|
||||
origin = new Vec3d(caster.posX, caster.getEntityBoundingBox().minY + caster.getEyeHeight(), caster.posZ);
|
||||
|
||||
if(caster instanceof EntityPlayer){
|
||||
WizardData data = WizardData.get((EntityPlayer)caster);
|
||||
if(data != null){
|
||||
RayTraceResult hit = data.getVariable(TARGET_KEY);
|
||||
if(hit != null) target = hit.hitVec;
|
||||
}
|
||||
}else if(caster instanceof EntityLiving){
|
||||
Entity entity = ((EntityLiving)caster).getAttackTarget();
|
||||
if(entity != null) target = new Vec3d(entity.posX, entity.getEntityBoundingBox().minY + entity.height/2, entity.posZ);
|
||||
}
|
||||
|
||||
if(target != null) direction = target.subtract(origin).normalize();
|
||||
|
||||
this.playSound(world, caster, duration, duration, modifiers, "release");
|
||||
|
||||
}else if(!Double.isNaN(x) && !Double.isNaN(y) && !Double.isNaN(z)){
|
||||
|
||||
origin = new Vec3d(x, y, z);
|
||||
direction = new Vec3d(facing.getDirectionVec());
|
||||
RayTraceResult result = findTarget(world, null, origin, direction, modifiers);
|
||||
target = result.hitVec;
|
||||
|
||||
this.playSound(world, origin, duration, duration, modifiers, "release");
|
||||
}
|
||||
|
||||
if(world.isRemote && origin != null && direction != null){
|
||||
|
||||
float extensionSpeed = getProperty(EXTENSION_SPEED).floatValue() * modifiers.get(SpellModifiers.POTENCY);
|
||||
double distance = Math.min(target.subtract(origin).length(), duration * extensionSpeed);
|
||||
|
||||
spawnLeafParticles(world, origin, direction, distance);
|
||||
}
|
||||
}
|
||||
|
||||
private void spawnLeafParticles(World world, Vec3d origin, Vec3d direction, double distance){
|
||||
// Copied from SpellRay
|
||||
for(double d = PARTICLE_SPACING; d <= distance; d += PARTICLE_SPACING){
|
||||
double x = origin.x + d*direction.x;// + PARTICLE_JITTER * (world.rand.nextDouble()*2 - 1);
|
||||
double y = origin.y + d*direction.y;// + PARTICLE_JITTER * (world.rand.nextDouble()*2 - 1);
|
||||
double z = origin.z + d*direction.z;// + PARTICLE_JITTER * (world.rand.nextDouble()*2 - 1);
|
||||
ParticleBuilder.create(Type.LEAF, world.rand, x, y, z, PARTICLE_JITTER, true).time(25 + world.rand.nextInt(5)).spawn(world);
|
||||
}
|
||||
}
|
||||
|
||||
private RayTraceResult findTarget(World world, @Nullable EntityLivingBase caster, Vec3d origin, Vec3d direction, SpellModifiers modifiers){
|
||||
|
||||
double range = getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade);
|
||||
|
||||
Vec3d endpoint = origin.add(direction.scale(range));
|
||||
|
||||
RayTraceResult result = RayTracer.rayTrace(world, origin, endpoint, 0, false,
|
||||
true, false, Entity.class, RayTracer.ignoreEntityFilter(caster));
|
||||
|
||||
// Non-solid blocks (or if the result is null) count as misses
|
||||
if(result == null || result.typeOfHit == RayTraceResult.Type.BLOCK
|
||||
&& !world.getBlockState(result.getBlockPos()).getMaterial().isSolid()){
|
||||
return new RayTraceResult(RayTraceResult.Type.MISS, endpoint, EnumFacing.DOWN, new BlockPos(endpoint));
|
||||
// Immovable entities count as misses too, but the endpoint is the hit vector instead
|
||||
}else if(result.entityHit != null && !result.entityHit.canBePushed()){
|
||||
return new RayTraceResult(RayTraceResult.Type.MISS, result.hitVec, EnumFacing.DOWN, new BlockPos(endpoint));
|
||||
}
|
||||
// If the ray trace missed, result.hitVec will be the endpoint anyway - neat!
|
||||
return result;
|
||||
}
|
||||
|
||||
private static RayTraceResult update(EntityPlayer player, RayTraceResult grapplingTarget){
|
||||
|
||||
if(grapplingTarget != null && (!WizardryUtilities.isCasting(player, Spells.grapple)
|
||||
|| (grapplingTarget.entityHit != null && !grapplingTarget.entityHit.isEntityAlive()))){
|
||||
return null;
|
||||
}
|
||||
|
||||
return grapplingTarget;
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntityLargeFireball;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class GreaterFireball extends Spell {
|
||||
|
||||
public GreaterFireball(){
|
||||
super("greater_fireball", Tier.ADVANCED, Element.FIRE, SpellType.ATTACK, 20, 30, EnumAction.NONE, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesSpellRequirePacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
Vec3d look = caster.getLookVec();
|
||||
|
||||
if(!world.isRemote){
|
||||
EntityLargeFireball fireball = new EntityLargeFireball(world, caster, 1, 1, 1);
|
||||
fireball.setPosition(caster.posX + look.x, caster.posY + look.y + 1.3, caster.posZ + look.z);
|
||||
fireball.accelerationX = look.x * 0.1;
|
||||
fireball.accelerationY = look.y * 0.1;
|
||||
fireball.accelerationZ = look.z * 0.1;
|
||||
world.spawnEntity(fireball);
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1);
|
||||
caster.swingArm(hand);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target,
|
||||
SpellModifiers modifiers){
|
||||
|
||||
if(target != null){
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
EntityLargeFireball fireball = new EntityLargeFireball(world, caster, 1, 1, 1);
|
||||
|
||||
double dx = target.posX - caster.posX;
|
||||
double dy = target.getEntityBoundingBox().minY + (double)(target.height / 2.0F)
|
||||
- (caster.posY + (double)(caster.height / 2.0F));
|
||||
double dz = target.posZ - caster.posZ;
|
||||
|
||||
fireball.accelerationX = dx / caster.getDistance(target) * 0.1;
|
||||
fireball.accelerationY = dy / caster.getDistance(target) * 0.1;
|
||||
fireball.accelerationZ = dz / caster.getDistance(target) * 0.1;
|
||||
|
||||
fireball.setPosition(caster.posX, caster.posY + caster.getEyeHeight(), caster.posZ);
|
||||
|
||||
world.spawnEntity(fireball);
|
||||
}
|
||||
|
||||
caster.playSound(SoundEvents.ENTITY_BLAZE_SHOOT, 1, 1);
|
||||
caster.swingArm(hand);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByNPCs(){
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +1,21 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
|
||||
public class GreaterHeal extends SpellBuff {
|
||||
|
||||
public GreaterHeal(){
|
||||
super("greater_heal", Tier.ADVANCED, Element.HEALING, SpellType.DEFENCE, 15, 40, WizardrySounds.SPELL_HEAL, 1, 1, 0.3f);
|
||||
super("greater_heal", 1, 1, 0.3f);
|
||||
this.soundValues(0.7f, 1.2f, 0.4f);
|
||||
addProperties(HEALTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean applyEffects(EntityLivingBase caster, SpellModifiers modifiers){
|
||||
|
||||
if(caster.getHealth() < caster.getMaxHealth() && caster.getHealth() > 0){
|
||||
caster.heal(8 * modifiers.get(SpellModifiers.POTENCY));
|
||||
caster.heal(getProperty(HEALTH).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.entity.EntityLevitatingBlock;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.item.EntityTNTPrimed;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.network.play.server.SPacketEntityVelocity;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class GreaterTelekinesis extends SpellRay {
|
||||
|
||||
public static final String HOLD_RANGE = "hold_range";
|
||||
public static final String THROW_VELOCITY = "throw_velocity";
|
||||
|
||||
/** Makes things a bit smoother-looking / 'realistic'. */
|
||||
private static final float UNDERSHOOT = 0.2f;
|
||||
|
||||
public GreaterTelekinesis(){
|
||||
super("greater_telekinesis", true, EnumAction.NONE);
|
||||
this.aimAssist(0.4f);
|
||||
this.particleSpacing(1);
|
||||
this.particleJitter(0.05);
|
||||
this.particleVelocity(0.3);
|
||||
addProperties(HOLD_RANGE, THROW_VELOCITY, DAMAGE);
|
||||
this.soundValues(0.8f, 1, 0.2f);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs() { return false; }
|
||||
@Override public boolean canBeCastByDispensers() { return false; }
|
||||
|
||||
@Override
|
||||
protected SoundEvent[] createSounds(){
|
||||
return this.createContinuousSpellSounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, EntityLivingBase entity, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, entity, ticksInUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, double x, double y, double z, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, x, y, z, ticksInUse, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
// Can't be cast by dispensers so we know caster isn't null, but just in case...
|
||||
if(caster != null && (target instanceof EntityLivingBase || target instanceof EntityLevitatingBlock || target instanceof EntityTNTPrimed)){
|
||||
|
||||
if(target instanceof EntityPlayer && ((caster instanceof EntityPlayer && !Wizardry.settings.playersMoveEachOther)
|
||||
|| ItemArtefact.isArtefactActive((EntityPlayer)target, WizardryItems.amulet_anchoring))){
|
||||
|
||||
if(!world.isRemote && caster instanceof EntityPlayer) ((EntityPlayer)caster).sendStatusMessage(
|
||||
new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted()), true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(target instanceof EntityLevitatingBlock){
|
||||
((EntityLevitatingBlock)target).suspend();
|
||||
((EntityLevitatingBlock)target).setCaster(caster); // Yep, you can steal other players' blocks in mid-air!
|
||||
}
|
||||
|
||||
Vec3d targetPos = target.getPositionVector().add(0, target.height/2, 0);
|
||||
|
||||
if(caster.isSneaking()){
|
||||
|
||||
Vec3d look = caster.getLookVec().scale(getProperty(THROW_VELOCITY).floatValue() * modifiers.get(WizardryItems.range_upgrade));
|
||||
target.addVelocity(look.x, look.y, look.z);
|
||||
if(caster instanceof EntityPlayer) caster.swingArm(caster.getActiveHand());
|
||||
|
||||
}else{
|
||||
|
||||
WizardryUtilities.undoGravity(target);
|
||||
|
||||
// The following code extrapolates the entity's current velocity to determine whether it will pass the
|
||||
// target position in the next tick, and adds or subtracts velocity accordingly.
|
||||
|
||||
Vec3d vec = origin.add(caster.getLookVec().scale(getProperty(HOLD_RANGE).floatValue()));
|
||||
|
||||
Vec3d velocity = vec.subtract(targetPos).subtract(target.motionX, target.motionY, target.motionZ)
|
||||
.scale(1 - UNDERSHOOT);
|
||||
|
||||
target.addVelocity(velocity.x, velocity.y, velocity.z);
|
||||
}
|
||||
|
||||
// Player motion is handled on that player's client so needs packets
|
||||
if(target instanceof EntityPlayerMP){
|
||||
((EntityPlayerMP)target).connection.sendPacket(new SPacketEntityVelocity(target));
|
||||
}
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
ParticleBuilder.create(Type.BEAM).entity(caster).clr(0.2f, 0.6f + 0.3f * world.rand.nextFloat(), 1)
|
||||
.pos(origin.subtract(caster.getPositionVector())).target(target).time(0)
|
||||
.scale(MathHelper.sin(ticksInUse * 0.3f) * 0.1f + 0.9f).spawn(world);
|
||||
|
||||
if(ticksInUse % 18 == 1) ParticleBuilder.create(Type.FLASH).entity(target).pos(0, target.height/2, 0)
|
||||
.scale(2.5f).time(30).clr(0.2f, 0.8f, 1).fade(1f, 1f, 1f).spawn(world);
|
||||
|
||||
ParticleBuilder.create(Type.SPARKLE, target).vel(0, 0.05, 0).time(15).scale(0.6f).clr(0.2f, 0.6f, 1)
|
||||
.fade(1f, 1f, 1f).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){
|
||||
|
||||
if(WizardryUtilities.canDamageBlocks(caster, world) && !WizardryUtilities.isBlockUnbreakable(world, pos)
|
||||
&& world.getBlockState(pos).getMaterial().isSolid()
|
||||
&& (world.getTileEntity(pos) == null || !world.getTileEntity(pos).getTileData().hasUniqueId(ArcaneLock.NBT_KEY))){
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
EntityLevitatingBlock block = new EntityLevitatingBlock(world, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5,
|
||||
world.getBlockState(pos));
|
||||
|
||||
block.fallTime = 1;
|
||||
block.damageMultiplier = modifiers.get(SpellModifiers.POTENCY);
|
||||
block.setCaster(caster);
|
||||
|
||||
world.spawnEntity(block);
|
||||
world.setBlockToAir(pos);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,16 +1,8 @@
|
||||
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.ISummonedCreature;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.AllyDesignationSystem;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -19,10 +11,14 @@ import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GroupHeal extends Spell {
|
||||
|
||||
public GroupHeal(){
|
||||
super("group_heal", Tier.ADVANCED, Element.HEALING, SpellType.DEFENCE, 35, 150, EnumAction.BOW, false);
|
||||
super("group_heal", EnumAction.BOW, false);
|
||||
this.soundValues(0.7f, 1.2f, 0.4f);
|
||||
addProperties(EFFECT_RADIUS, HEALTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -30,64 +26,20 @@ public class GroupHeal extends Spell {
|
||||
|
||||
boolean flag = false;
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(
|
||||
5 * modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world);
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(getProperty(EFFECT_RADIUS).floatValue()
|
||||
* modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world);
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
|
||||
if(target instanceof EntityPlayer){
|
||||
if(target == caster || AllyDesignationSystem.isAllied(caster, target)){
|
||||
|
||||
if(WizardryUtilities.isPlayerAlly(caster, (EntityPlayer)target) || target == caster){
|
||||
if(target.getHealth() < target.getMaxHealth() && target.getHealth() > 0){
|
||||
|
||||
if(((EntityPlayer)target).shouldHeal()){
|
||||
target.heal(getProperty(HEALTH).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
target.heal(6 * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
for(int i = 0; i < 10; i++){
|
||||
double x = caster.posX + world.rand.nextDouble() * 2 - 1;
|
||||
double y = caster.getEntityBoundingBox().minY + caster.getEyeHeight() - 0.5 + world.rand.nextDouble();
|
||||
double z = caster.posZ + world.rand.nextDouble() * 2 - 1;
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.1, 0).clr(1, 1, 0.3f).spawn(world);
|
||||
}
|
||||
|
||||
ParticleBuilder.create(Type.BUFF).entity(caster).clr(1, 1, 0.3f).spawn(world);
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F,
|
||||
world.rand.nextFloat() * 0.4F + 1.0F);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Now also works on summoned creatures
|
||||
}else if(target instanceof ISummonedCreature){
|
||||
|
||||
EntityLivingBase summoner = ((ISummonedCreature)target).getCaster();
|
||||
|
||||
if(summoner == caster || (summoner instanceof EntityPlayer
|
||||
&& WizardryUtilities.isPlayerAlly(caster, (EntityPlayer)summoner))){
|
||||
|
||||
if(target.getHealth() < target.getMaxHealth() && target.getHealth() > 0){
|
||||
|
||||
target.heal(6 * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
if(world.isRemote){
|
||||
for(int i = 0; i < 10; i++){
|
||||
double x = caster.posX + world.rand.nextDouble() * 2 - 1;
|
||||
double y = caster.getEntityBoundingBox().minY + caster.getEyeHeight() - 0.5 + world.rand.nextDouble();
|
||||
double z = caster.posZ + world.rand.nextDouble() * 2 - 1;
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.1, 0).clr(1, 1, 0.3f).spawn(world);
|
||||
}
|
||||
|
||||
ParticleBuilder.create(Type.BUFF).entity(caster).clr(1, 1, 0.3f).spawn(world);
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F,
|
||||
world.rand.nextFloat() * 0.4F + 1.0F);
|
||||
flag = true;
|
||||
}
|
||||
if(world.isRemote) ParticleBuilder.spawnHealParticles(world, target);
|
||||
playSound(world, target, ticksInUse, -1, modifiers);
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.IGrowable;
|
||||
@@ -15,14 +13,18 @@ import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class GrowthAura extends Spell {
|
||||
|
||||
public GrowthAura(){
|
||||
super("growth_aura", Tier.APPRENTICE, Element.EARTH, SpellType.UTILITY, 20, 50, EnumAction.NONE, false);
|
||||
super("growth_aura", EnumAction.NONE, false);
|
||||
addProperties(EFFECT_RADIUS);
|
||||
soundValues(0.7f, 1.2f, 0.2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesSpellRequirePacket(){
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -31,45 +33,43 @@ public class GrowthAura extends Spell {
|
||||
|
||||
boolean flag = false;
|
||||
|
||||
for(int i = -2; i < 1; i++){
|
||||
List<BlockPos> sphere = WizardryUtilities.getBlockSphere(caster.getPosition(),
|
||||
getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade));
|
||||
|
||||
for(int j = -1; j < 2; j++){
|
||||
for(BlockPos pos : sphere){
|
||||
|
||||
int x = (int)caster.posX + i;
|
||||
int y = WizardryUtilities.getNearestFloorLevelC(world,
|
||||
new BlockPos(caster.posX + i, caster.posY, caster.posZ + j), 2) - 1;
|
||||
int z = (int)caster.posZ + j;
|
||||
IBlockState state = world.getBlockState(pos);
|
||||
|
||||
BlockPos pos = new BlockPos(x, y, z);
|
||||
if(state.getBlock() instanceof IGrowable){
|
||||
|
||||
if(y > -1 && caster.getDistance(x, y, z) <= 2){
|
||||
IGrowable plant = (IGrowable)state.getBlock();
|
||||
|
||||
IBlockState state = world.getBlockState(pos);
|
||||
if(plant.canGrow(world, pos, state, world.isRemote)){
|
||||
|
||||
if(state.getBlock() instanceof IGrowable){
|
||||
|
||||
IGrowable igrowable = (IGrowable)state.getBlock();
|
||||
|
||||
if(igrowable.canGrow(world, pos, state, world.isRemote)){
|
||||
|
||||
if(!world.isRemote){
|
||||
if(igrowable.canUseBonemeal(world, world.rand, pos, state)){
|
||||
igrowable.grow(world, world.rand, pos, state);
|
||||
if(!world.isRemote){
|
||||
if(plant.canUseBonemeal(world, world.rand, pos, state)){
|
||||
if(world.rand.nextFloat() < 0.35f && ItemArtefact.isArtefactActive(caster, WizardryItems.charm_growth)){
|
||||
while(plant.canGrow(world, pos, state, false)){
|
||||
plant.grow(world, world.rand, pos, state);
|
||||
state = world.getBlockState(pos); // Update the state with the new one
|
||||
plant = (IGrowable)state.getBlock(); // Update the block with the new one
|
||||
}
|
||||
}else{
|
||||
// Yes, it's meant to be 0, and it automatically changes it to 15.
|
||||
ItemDye.spawnBonemealParticles(world, pos, 0);
|
||||
plant.grow(world, world.rand, pos, state);
|
||||
}
|
||||
|
||||
flag = true;
|
||||
}
|
||||
}else{
|
||||
// Yes, it's meant to be 0, and it automatically changes it to 15.
|
||||
ItemDye.spawnBonemealParticles(world, pos, 0);
|
||||
}
|
||||
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(flag) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_HEAL, 0.7F,
|
||||
world.rand.nextFloat() * 0.4F + 1.0F);
|
||||
if(flag) this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.construct.EntityHailstorm;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Hailstorm extends SpellConstructRanged<EntityHailstorm> {
|
||||
|
||||
public Hailstorm(){
|
||||
super("hailstorm", Tier.MASTER, Element.ICE, SpellType.ATTACK, 75, 300, EntityHailstorm::new, 120, 20, WizardrySounds.SPELL_ICE);
|
||||
super("hailstorm", EntityHailstorm::new, false);
|
||||
this.floor(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean spawnConstruct(World world, double x, double y, double z, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
protected boolean spawnConstruct(World world, double x, double y, double z, EnumFacing side, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
|
||||
// Moves the entity back towards the caster a bit, so the area of effect is better centred on the position.
|
||||
// 3 is the distance to move the entity back towards the caster.
|
||||
@@ -29,13 +26,13 @@ public class Hailstorm extends SpellConstructRanged<EntityHailstorm> {
|
||||
// Moves the entity up 5 blocks so that it is above mobs' heads.
|
||||
y += 5;
|
||||
|
||||
return super.spawnConstruct(world, x, y, z, caster, modifiers);
|
||||
return super.spawnConstruct(world, x, y, z, side, caster, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConstructExtras(EntityHailstorm construct, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
protected void addConstructExtras(EntityHailstorm construct, EnumFacing side, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
// Makes the arrows shoot in the direction the caster was looking when they cast the spell.
|
||||
construct.rotationYaw = caster.rotationYawHead;
|
||||
if(caster != null) construct.rotationYaw = caster.rotationYawHead;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,24 +1,21 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
|
||||
public class Heal extends SpellBuff {
|
||||
|
||||
public Heal(){
|
||||
super("heal", Tier.BASIC, Element.HEALING, SpellType.DEFENCE, 5, 20, WizardrySounds.SPELL_HEAL, 1, 1, 0.3f);
|
||||
super("heal", 1, 1, 0.3f);
|
||||
this.soundValues(0.7f, 1.2f, 0.4f);
|
||||
addProperties(HEALTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean applyEffects(EntityLivingBase caster, SpellModifiers modifiers){
|
||||
|
||||
if(caster.getHealth() < caster.getMaxHealth() && caster.getHealth() > 0){
|
||||
caster.heal(4 * modifiers.get(SpellModifiers.POTENCY));
|
||||
caster.heal(getProperty(HEALTH).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class HealAlly extends SpellRay {
|
||||
|
||||
public HealAlly(){
|
||||
super("heal_ally", Tier.APPRENTICE, Element.HEALING, SpellType.DEFENCE, 10, 20, false, 10, WizardrySounds.SPELL_HEAL);
|
||||
super("heal_ally", false, EnumAction.NONE);
|
||||
this.soundValues(0.7f, 1.2f, 0.4f);
|
||||
addProperties(HEALTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
@@ -31,21 +28,10 @@ public class HealAlly extends SpellRay {
|
||||
|
||||
if(entity.getHealth() < entity.getMaxHealth() && entity.getHealth() > 0){
|
||||
|
||||
entity.heal((int)(5 * modifiers.get(SpellModifiers.POTENCY)));
|
||||
entity.heal(getProperty(HEALTH).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
float r = 1; float g = 1; float b = 0.3f;
|
||||
|
||||
for(int i = 0; i < 10; i++){
|
||||
double x1 = (double)((float)entity.posX + world.rand.nextFloat() * 2 - 1.0f);
|
||||
double y1 = (double)((float)entity.getEntityBoundingBox().minY + entity.getEyeHeight() - 0.5f + world.rand.nextFloat());
|
||||
double z1 = (double)((float)entity.posZ + world.rand.nextFloat() * 2 - 1.0f);
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x1, y1, z1).vel(0, 0.1F, 0).clr(r, g, b).spawn(world);
|
||||
}
|
||||
|
||||
ParticleBuilder.create(Type.BUFF).entity(caster).clr(r, g, b).spawn(world);
|
||||
}
|
||||
if(world.isRemote) ParticleBuilder.spawnHealParticles(world, entity);
|
||||
playSound(world, entity, ticksInUse, -1, modifiers);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -55,12 +41,12 @@ public class HealAlly extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
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, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,163 +1,89 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.block.BlockStatue;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.tileentity.TileEntityStatue;
|
||||
import electroblob.wizardry.util.AllyDesignationSystem;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.monster.EntityBlaze;
|
||||
import net.minecraft.entity.monster.EntityMagmaCube;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class IceAge extends Spell {
|
||||
|
||||
private static final int baseDuration = 1200;
|
||||
|
||||
public IceAge(){
|
||||
super("ice_age", Tier.MASTER, Element.ICE, SpellType.ATTACK, 70, 250, EnumAction.BOW, false);
|
||||
super("ice_age", EnumAction.BOW, false);
|
||||
this.soundValues(0.7f, 1.0f, 0);
|
||||
addProperties(EFFECT_RADIUS, EFFECT_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesSpellRequirePacket(){
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(
|
||||
7 * modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world);
|
||||
float radius = getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade);
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(radius, caster.posX, caster.posY, caster.posZ, world);
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
if(WizardryUtilities.isValidTarget(caster, target)){
|
||||
if(AllyDesignationSystem.isValidTarget(caster, target)){
|
||||
if(!world.isRemote){
|
||||
if(target instanceof EntityBlaze || target instanceof EntityMagmaCube){
|
||||
// These have been removed for the time being because they cause the entity to sink into the
|
||||
// floor when it breaks out.
|
||||
// target.attackEntityFrom(WizardryUtilities.causePlayerMagicDamage(entityplayer), 8.0f *
|
||||
// modifiers.get(SpellModifiers.DAMAGE));
|
||||
}else{
|
||||
// target.attackEntityFrom(WizardryUtilities.causePlayerMagicDamage(entityplayer), 4.0f *
|
||||
// modifiers.get(SpellModifiers.DAMAGE));
|
||||
}
|
||||
if(target.isBurning()){
|
||||
target.extinguish();
|
||||
}
|
||||
|
||||
if(target instanceof EntityBlaze) WizardryAdvancementTriggers.freeze_blaze.triggerFor(caster);
|
||||
|
||||
if(target instanceof EntityLiving){
|
||||
|
||||
// Stops the entity looking red while frozen and the resulting z-fighting
|
||||
target.hurtTime = 0;
|
||||
|
||||
BlockPos pos = new BlockPos(target);
|
||||
|
||||
// Short mobs such as spiders and pigs
|
||||
if((target.height < 1.2 || target.isChild())
|
||||
&& WizardryUtilities.canBlockBeReplaced(world, pos)){
|
||||
world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState());
|
||||
if(world.getTileEntity(pos) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart((EntityLiving)target, 1,
|
||||
1);
|
||||
((TileEntityStatue)world.getTileEntity(pos)).setLifetime(
|
||||
(int)(baseDuration * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
}
|
||||
target.setDead();
|
||||
target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F);
|
||||
}
|
||||
// Normal sized mobs like zombies and skeletons
|
||||
else if(target.height < 2.5 && WizardryUtilities.canBlockBeReplaced(world, pos)
|
||||
&& WizardryUtilities.canBlockBeReplaced(world, pos.up())){
|
||||
world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState());
|
||||
if(world.getTileEntity(pos) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart((EntityLiving)target, 1,
|
||||
2);
|
||||
((TileEntityStatue)world.getTileEntity(pos)).setLifetime(
|
||||
(int)(baseDuration * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
}
|
||||
|
||||
world.setBlockState(pos.up(), WizardryBlocks.ice_statue.getDefaultState());
|
||||
if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos.up()))
|
||||
.setCreatureAndPart((EntityLiving)target, 2, 2);
|
||||
}
|
||||
target.setDead();
|
||||
target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F);
|
||||
}
|
||||
// Tall mobs like endermen
|
||||
else if(WizardryUtilities.canBlockBeReplaced(world, pos)
|
||||
&& WizardryUtilities.canBlockBeReplaced(world, pos.up())
|
||||
&& WizardryUtilities.canBlockBeReplaced(world, pos.up(2))){
|
||||
world.setBlockState(pos, WizardryBlocks.ice_statue.getDefaultState());
|
||||
if(world.getTileEntity(pos) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos)).setCreatureAndPart((EntityLiving)target, 1,
|
||||
3);
|
||||
((TileEntityStatue)world.getTileEntity(pos)).setLifetime(
|
||||
(int)(baseDuration * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
}
|
||||
|
||||
world.setBlockState(pos.up(), WizardryBlocks.ice_statue.getDefaultState());
|
||||
if(world.getTileEntity(pos.up()) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos.up()))
|
||||
.setCreatureAndPart((EntityLiving)target, 2, 3);
|
||||
}
|
||||
|
||||
world.setBlockState(pos.up(2), WizardryBlocks.ice_statue.getDefaultState());
|
||||
if(world.getTileEntity(pos.up(2)) instanceof TileEntityStatue){
|
||||
((TileEntityStatue)world.getTileEntity(pos.up(2)))
|
||||
.setCreatureAndPart((EntityLiving)target, 3, 3);
|
||||
}
|
||||
target.setDead();
|
||||
target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F);
|
||||
if(((BlockStatue)WizardryBlocks.ice_statue).convertToStatue((EntityLiving)target,
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)))){
|
||||
target.playSound(WizardrySounds.MISC_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!world.isRemote){
|
||||
for(int i = -7; i < 8; i++){
|
||||
for(int j = -7; j < 8; j++){
|
||||
if(!world.isRemote && WizardryUtilities.canDamageBlocks(caster, world)){
|
||||
for(int i = -(int)radius; i < (int)radius + 1; i++){
|
||||
for(int j = -(int)radius; j < (int)radius + 1; j++){
|
||||
|
||||
BlockPos pos = new BlockPos(caster).add(i, 0, j);
|
||||
|
||||
int y = WizardryUtilities.getNearestFloorLevelB(world, new BlockPos(pos), 7);
|
||||
Integer y = WizardryUtilities.getNearestSurface(world, new BlockPos(pos), EnumFacing.UP, (int)radius, true, WizardryUtilities.SurfaceCriteria.BUILDABLE);
|
||||
|
||||
pos = new BlockPos(pos.getX(), y, pos.getZ());
|
||||
if(y != null){
|
||||
|
||||
double dist = caster.getDistance((int)caster.posX + i, y, (int)caster.posZ + j);
|
||||
pos = new BlockPos(pos.getX(), y, pos.getZ());
|
||||
|
||||
// Randomised with weighting so that the nearer the block the more likely it is to be snowed.
|
||||
if(y != -1 && world.rand.nextInt((int)dist * 2 + 1) < 7 && dist < 8){
|
||||
if(world.getBlockState(pos.down()) == Blocks.WATER.getDefaultState()){
|
||||
world.setBlockState(pos.down(), Blocks.ICE.getDefaultState());
|
||||
}else if(world.getBlockState(pos.down()) == Blocks.LAVA.getDefaultState()){
|
||||
world.setBlockState(pos.down(), Blocks.OBSIDIAN.getDefaultState());
|
||||
}else if(world.getBlockState(pos.down()) == Blocks.FLOWING_LAVA.getDefaultState()){
|
||||
world.setBlockState(pos.down(), Blocks.COBBLESTONE.getDefaultState());
|
||||
}else if(Blocks.SNOW_LAYER.canPlaceBlockAt(world, pos)){
|
||||
world.setBlockState(pos, Blocks.SNOW_LAYER.getDefaultState());
|
||||
double dist = caster.getDistance((int)caster.posX + i, y, (int)caster.posZ + j);
|
||||
|
||||
// Randomised with weighting so that the nearer the block the more likely it is to be snowed.
|
||||
if(y != -1 && world.rand.nextInt((int)dist * 2 + 1) < radius && dist < radius){
|
||||
if(world.getBlockState(pos.down()) == Blocks.WATER.getDefaultState()){
|
||||
world.setBlockState(pos.down(), Blocks.ICE.getDefaultState());
|
||||
}else if(world.getBlockState(pos.down()) == Blocks.LAVA.getDefaultState()){
|
||||
world.setBlockState(pos.down(), Blocks.OBSIDIAN.getDefaultState());
|
||||
}else if(world.getBlockState(pos.down()) == Blocks.FLOWING_LAVA.getDefaultState()){
|
||||
world.setBlockState(pos.down(), Blocks.COBBLESTONE.getDefaultState());
|
||||
}else if(Blocks.SNOW_LAYER.canPlaceBlockAt(world, pos)){
|
||||
world.setBlockState(pos, Blocks.SNOW_LAYER.getDefaultState());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_ICE, 0.7F, 1.0f);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_WIND, 1.0F, 1.0f);
|
||||
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,49 +1,62 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.construct.EntityIceSpike;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class IceSpikes extends SpellConstructRanged<EntityIceSpike> {
|
||||
|
||||
private static final int BASE_SPAWN_COUNT = 17; // Was 18, reduced to 17 to account for the extra one in the middle.
|
||||
public static final String ICE_SPIKE_COUNT = "ice_spike_count";
|
||||
|
||||
public IceSpikes(){
|
||||
// Base duration is set to -1 so that the superclass doesn't waste time retrieving the duration multiplier
|
||||
super("ice_spikes", Tier.ADVANCED, Element.ICE, SpellType.ATTACK, 30, 75, EntityIceSpike::new, -1, 20, WizardrySounds.SPELL_ICE);
|
||||
super("ice_spikes", EntityIceSpike::new, true);
|
||||
addProperties(EFFECT_RADIUS, ICE_SPIKE_COUNT, DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH);
|
||||
this.ignoreUncollidables(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean spawnConstruct(World world, double x, double y, double z, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
|
||||
if(world.getBlockState(new BlockPos(x, y, z)).isNormalCube()) return false;
|
||||
protected boolean spawnConstruct(World world, double x, double y, double z, EnumFacing side, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
|
||||
BlockPos blockHit = new BlockPos(x, y, z);
|
||||
if(side != null && side.getAxisDirection() == EnumFacing.AxisDirection.NEGATIVE) blockHit = blockHit.offset(side);
|
||||
|
||||
if(world.getBlockState(blockHit).isNormalCube()) return false;
|
||||
|
||||
Vec3d origin = new Vec3d(x, y, z);
|
||||
|
||||
Vec3d pos = origin.add(new Vec3d(side.getOpposite().getDirectionVec()));
|
||||
|
||||
// Now always spawns a spike exactly at the position aimed at
|
||||
super.spawnConstruct(world, x, y-1, z, caster, modifiers);
|
||||
|
||||
int quantity = (int)(BASE_SPAWN_COUNT * modifiers.get(WizardryItems.blast_upgrade));
|
||||
super.spawnConstruct(world, pos.x, pos.y, pos.z, side, caster, modifiers);
|
||||
// -1 because of the one spawned above
|
||||
int quantity = (int)(getProperty(ICE_SPIKE_COUNT).floatValue() * modifiers.get(WizardryItems.blast_upgrade)) - 1;
|
||||
|
||||
float maxRadius = getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade);
|
||||
|
||||
for(int i=0; i<quantity; i++){
|
||||
|
||||
float angle = (float)(world.rand.nextFloat() * Math.PI * 2);
|
||||
double radius = 0.5 + world.rand.nextDouble() * 2 * modifiers.get(WizardryItems.blast_upgrade);
|
||||
|
||||
double x1 = x + radius * MathHelper.sin(angle);
|
||||
double z1 = z + radius * MathHelper.cos(angle);
|
||||
double y1 = WizardryUtilities.getNearestFloorLevel(world,
|
||||
new BlockPos(MathHelper.floor(x1), (int)y, MathHelper.floor(z1)), 2) - 1;
|
||||
double radius = 0.5 + world.rand.nextDouble() * (maxRadius - 0.5);
|
||||
|
||||
if(y1 > -1){
|
||||
super.spawnConstruct(world, x1, y1, z1, caster, modifiers);
|
||||
// First, generate a random vector of length radius with a z component of zero
|
||||
// Then rotate it so that what was south is now the side that was hit
|
||||
Vec3d offset = Vec3d.fromPitchYaw(world.rand.nextFloat() * 180 - 90, world.rand.nextBoolean() ? 0 : 180)
|
||||
.scale(radius).rotateYaw(side.getHorizontalAngle() * (float)Math.PI/180).rotatePitch(WizardryUtilities.getPitch(side) * (float)Math.PI/180);
|
||||
|
||||
if(side.getAxis().isHorizontal()) offset = offset.rotateYaw((float)Math.PI/2);
|
||||
|
||||
Integer surface = WizardryUtilities.getNearestSurface(world, new BlockPos(origin.add(offset)), side,
|
||||
(int)maxRadius, true, WizardryUtilities.SurfaceCriteria.basedOn(World::isBlockFullCube));
|
||||
|
||||
if(surface != null){
|
||||
Vec3d vec = WizardryUtilities.replaceComponent(origin.add(offset), side.getAxis(), surface)
|
||||
.subtract(new Vec3d(side.getDirectionVec()));
|
||||
super.spawnConstruct(world, vec.x, vec.y, vec.z, side, caster, modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,9 +64,10 @@ public class IceSpikes extends SpellConstructRanged<EntityIceSpike> {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConstructExtras(EntityIceSpike construct, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
protected void addConstructExtras(EntityIceSpike construct, EnumFacing side, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
// In this particular case, lifetime is implemented as a delay instead so is treated differently.
|
||||
construct.lifetime = 30 + construct.world.rand.nextInt(15);
|
||||
construct.setFacing(side);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,54 +1,56 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.block.BlockStatue;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class IceStatue extends SpellRay {
|
||||
|
||||
private static final int BASE_DURATION = 400;
|
||||
|
||||
public IceStatue(){
|
||||
super("ice_statue", Tier.APPRENTICE, Element.ICE, SpellType.ATTACK, 15, 40, false, 10, WizardrySounds.SPELL_ICE);
|
||||
super("ice_statue", false, EnumAction.NONE);
|
||||
this.soundValues(1, 1.4f, 0.4f);
|
||||
addProperties(EFFECT_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected SoundEvent[] createSounds(){
|
||||
return createSoundsWithSuffixes("shoot", "freeze");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(target instanceof EntityLiving && !world.isRemote){
|
||||
// Unchecked cast is fine because the block is a static final field
|
||||
if(((BlockStatue)WizardryBlocks.ice_statue).convertToStatue((EntityLiving)target,
|
||||
(int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)))){
|
||||
|
||||
target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F);
|
||||
return true;
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)))){
|
||||
|
||||
//target.playSound(WizardrySounds.SPELL_FREEZE, 1.0F, world.rand.nextFloat() * 0.4F + 0.8F);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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 onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,34 +1,32 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.Blocks;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Ignite extends SpellRay {
|
||||
|
||||
/** The base duration for which entities are set on fire by this spell. */
|
||||
private static final int BASE_DURATION = 10;
|
||||
|
||||
public Ignite(){
|
||||
super("ignite", Tier.BASIC, Element.FIRE, SpellType.ATTACK, 5, 10, false, 10, SoundEvents.ITEM_FLINTANDSTEEL_USE);
|
||||
super("ignite", false, EnumAction.NONE);
|
||||
this.soundValues(1, 1, 0.4f);
|
||||
addProperties(BURN_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
// Fire can damage armour stands, so this includes them
|
||||
if(target instanceof EntityLivingBase) {
|
||||
|
||||
@@ -36,7 +34,7 @@ public class Ignite extends SpellRay {
|
||||
if(!world.isRemote && caster instanceof EntityPlayer) ((EntityPlayer)caster).sendStatusMessage(
|
||||
new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted()), true);
|
||||
}else{
|
||||
target.setFire((int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
target.setFire((int)(getProperty(BURN_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -46,8 +44,10 @@ public class Ignite extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(!WizardryUtilities.canDamageBlocks(caster, world)) return false;
|
||||
|
||||
pos = pos.offset(side);
|
||||
|
||||
if(world.isAirBlock(pos)){
|
||||
@@ -63,7 +63,7 @@ public class Ignite extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,27 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.registry.WizardryEnchantments;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.ItemBow;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.ItemSword;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class ImbueWeapon extends Spell {
|
||||
|
||||
public ImbueWeapon(){
|
||||
super("imbue_weapon", Tier.APPRENTICE, Element.SORCERY, SpellType.UTILITY, 20, 50, EnumAction.BOW, false);
|
||||
super("imbue_weapon", EnumAction.BOW, false);
|
||||
addProperties(EFFECT_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,29 +32,29 @@ public class ImbueWeapon extends Spell {
|
||||
|
||||
for(ItemStack stack : WizardryUtilities.getPrioritisedHotbarAndOffhand(caster)){
|
||||
|
||||
if(stack.getItem() instanceof ItemSword
|
||||
if(isSword(stack.getItem())
|
||||
&& !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.magic_sword)
|
||||
&& WizardData.get(caster).getImbuementDuration(WizardryEnchantments.magic_sword) <= 0){
|
||||
// The enchantment level as determined by the damage multiplier. The + 0.5f is so that
|
||||
// weird float processing doesn't incorrectly round it down.
|
||||
stack.addEnchantment(WizardryEnchantments.magic_sword, modifiers.get(SpellModifiers.POTENCY) == 1.0f
|
||||
? 1
|
||||
: (int)((modifiers.get(SpellModifiers.POTENCY) - 1.0f) / Constants.DAMAGE_INCREASE_PER_TIER
|
||||
: (int)((modifiers.get(SpellModifiers.POTENCY) - 1.0f) / Constants.POTENCY_INCREASE_PER_TIER
|
||||
+ 0.5f));
|
||||
WizardData.get(caster).setImbuementDuration(WizardryEnchantments.magic_sword,
|
||||
(int)(900 * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
|
||||
}else if(stack.getItem() instanceof ItemBow
|
||||
}else if(isBow(stack.getItem())
|
||||
&& !EnchantmentHelper.getEnchantments(stack).containsKey(WizardryEnchantments.magic_bow)
|
||||
&& WizardData.get(caster).getImbuementDuration(WizardryEnchantments.magic_bow) <= 0){
|
||||
// The enchantment level as determined by the damage multiplier. The + 0.5f is so that
|
||||
// weird float processing doesn't incorrectly round it down.
|
||||
stack.addEnchantment(WizardryEnchantments.magic_bow, modifiers.get(SpellModifiers.POTENCY) == 1.0f
|
||||
? 1
|
||||
: (int)((modifiers.get(SpellModifiers.POTENCY) - 1.0f) / Constants.DAMAGE_INCREASE_PER_TIER
|
||||
: (int)((modifiers.get(SpellModifiers.POTENCY) - 1.0f) / Constants.POTENCY_INCREASE_PER_TIER
|
||||
+ 0.5f));
|
||||
WizardData.get(caster).setImbuementDuration(WizardryEnchantments.magic_bow,
|
||||
(int)(900 * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
|
||||
}else{
|
||||
continue;
|
||||
@@ -72,11 +69,21 @@ public class ImbueWeapon extends Spell {
|
||||
}
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1, 1);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Returns true if the given item counts as a sword, i.e. it extends {@link ItemSword} or is in the whitelist. */
|
||||
public static boolean isSword(Item item){
|
||||
return item instanceof ItemSword || Arrays.asList(Wizardry.settings.swordItemWhitelist).contains(item.getRegistryName());
|
||||
}
|
||||
|
||||
/** Returns true if the given item counts as a bow, i.e. it extends {@link ItemBow} or is in the whitelist. */
|
||||
public static boolean isBow(Item item){
|
||||
return item instanceof ItemBow || Arrays.asList(Wizardry.settings.bowItemWhitelist).contains(item.getRegistryName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -13,11 +8,9 @@ import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityCreature;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.ai.RandomPositionGenerator;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.pathfinding.PathPoint;
|
||||
@@ -29,17 +22,22 @@ import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class Intimidate extends Spell {
|
||||
|
||||
/** The NBT tag name for storing the feared entity's UUID in the target's tag compound. */
|
||||
public static final String NBT_KEY = "fearedEntity";
|
||||
|
||||
private static final double BASE_RANGE = 8;
|
||||
private static final int BASE_DURATION = 600;
|
||||
|
||||
// These aren't spell properties because they're part fo the actual potion effect, not the spell itself.
|
||||
// However, the avoid distance can be modified using the potion amplifier.
|
||||
private static final double BASE_AVOID_DISTANCE = 16;
|
||||
private static final double AVOID_DISTANCE_PER_LEVEL = 4;
|
||||
|
||||
public Intimidate(){
|
||||
super("intimidate", Tier.APPRENTICE, Element.NECROMANCY, SpellType.ATTACK, 20, 100, EnumAction.BOW, false);
|
||||
super("intimidate", EnumAction.BOW, false);
|
||||
addProperties(EFFECT_RADIUS, EFFECT_DURATION, EFFECT_STRENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -48,19 +46,21 @@ public class Intimidate extends Spell {
|
||||
if(!world.isRemote){
|
||||
|
||||
List<EntityCreature> entities = WizardryUtilities.getEntitiesWithinRadius(
|
||||
BASE_RANGE * modifiers.get(WizardryItems.range_upgrade), caster.posX, caster.posY, caster.posZ,
|
||||
world, EntityCreature.class);
|
||||
getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.range_upgrade),
|
||||
caster.posX, caster.posY, caster.posZ, world, EntityCreature.class);
|
||||
|
||||
for(EntityCreature target : entities){
|
||||
// Why do we need this here?
|
||||
//runAway(target, caster);
|
||||
|
||||
runAway(target, caster);
|
||||
int bonusAmplifier = SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
NBTTagCompound entityNBT = target.getEntityData();
|
||||
if(entityNBT != null) entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID());
|
||||
|
||||
((EntityLiving)target).addPotionEffect(new PotionEffect(WizardryPotions.fear,
|
||||
(int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)), 0));
|
||||
|
||||
target.addPotionEffect(new PotionEffect(WizardryPotions.fear,
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)),
|
||||
getProperty(EFFECT_STRENGTH).intValue() + bonusAmplifier));
|
||||
}
|
||||
|
||||
}else{
|
||||
@@ -71,7 +71,7 @@ public class Intimidate extends Spell {
|
||||
ParticleBuilder.create(Type.DARK_MAGIC).pos(x, y, z).clr(0.9f, 0.1f, 0).spawn(world);
|
||||
}
|
||||
}
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERDRAGON_GROWL, 1.0f, 1.0f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -81,13 +81,14 @@ public class Intimidate extends Spell {
|
||||
*
|
||||
* @param target The entity running away
|
||||
* @param caster The entity that is being run away from
|
||||
* @param distance How far the entity will run from the caster
|
||||
* @return True if a new path was found and set, false if not.
|
||||
*/
|
||||
public static boolean runAway(EntityCreature target, EntityLivingBase caster){
|
||||
public static boolean runAway(EntityCreature target, EntityLivingBase caster, double distance){
|
||||
|
||||
if(target.getDistance(caster) < 16){
|
||||
if(target.getDistance(caster) < distance){
|
||||
|
||||
Vec3d Vec3d = RandomPositionGenerator.findRandomTargetBlockAwayFrom(target, 16, 7,
|
||||
Vec3d Vec3d = RandomPositionGenerator.findRandomTargetBlockAwayFrom(target, (int)distance, (int)(distance/2),
|
||||
new Vec3d(caster.posX, caster.posY, caster.posZ));
|
||||
|
||||
if(Vec3d == null){
|
||||
@@ -95,15 +96,14 @@ public class Intimidate extends Spell {
|
||||
|
||||
}else{
|
||||
// In both cases it is necessary to check if the entity already has a path so it doesn't change
|
||||
// direction
|
||||
// every tick, unless that path is towards the caster.
|
||||
// direction every tick, unless that path is towards the caster.
|
||||
// Path path = target.getNavigator().getPathToXYZ(Vec3d.xCoord, Vec3d.yCoord, Vec3d.zCoord);
|
||||
|
||||
boolean flag = true;
|
||||
|
||||
if(!target.getNavigator().noPath()){
|
||||
PathPoint point = target.getNavigator().getPath().getFinalPathPoint();
|
||||
if(point != null) flag = caster.getDistance(point.x, point.y, point.z) < 16;
|
||||
if(point != null) flag = caster.getDistance(point.x, point.y, point.z) < distance;
|
||||
}
|
||||
// Has a built in mind trick effect because for whatever reason this makes it work with skeletons.
|
||||
target.setAttackTarget(null);
|
||||
@@ -129,7 +129,9 @@ public class Intimidate extends Spell {
|
||||
Entity caster = WizardryUtilities.getEntityByUUID(creature.world, entityNBT.getUniqueId(NBT_KEY));
|
||||
|
||||
if(caster instanceof EntityLivingBase){
|
||||
runAway(creature, (EntityLivingBase)caster);
|
||||
double distance = BASE_AVOID_DISTANCE + AVOID_DISTANCE_PER_LEVEL
|
||||
* event.getEntityLiving().getActivePotionEffect(WizardryPotions.fear).getAmplifier();
|
||||
runAway(creature, (EntityLivingBase)caster, distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.AllyDesignationSystem;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
@@ -16,29 +11,34 @@ import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class InvigoratingPresence extends Spell {
|
||||
|
||||
private static final double BASE_RANGE = 5;
|
||||
private static final int BASE_DURATION = 900;
|
||||
|
||||
public InvigoratingPresence(){
|
||||
super("invigorating_presence", Tier.APPRENTICE, Element.HEALING, SpellType.UTILITY, 30, 60, EnumAction.BOW, false);
|
||||
super("invigorating_presence", EnumAction.BOW, false);
|
||||
this.soundValues(0.7f, 1.2f, 0.4f);
|
||||
addProperties(EFFECT_RADIUS, EFFECT_DURATION, EFFECT_STRENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
List<EntityPlayer> targets = WizardryUtilities.getEntitiesWithinRadius(
|
||||
BASE_RANGE * modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world,
|
||||
EntityPlayer.class);
|
||||
getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade),
|
||||
caster.posX, caster.posY, caster.posZ, world, EntityPlayer.class);
|
||||
|
||||
for(EntityPlayer target : targets){
|
||||
if(WizardryUtilities.isPlayerAlly(caster, target) || target == caster){
|
||||
// Strength 2 for 45 seconds.
|
||||
if(AllyDesignationSystem.isPlayerAlly(caster, target) || target == caster){
|
||||
|
||||
int bonusAmplifier = SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
target.addPotionEffect(new PotionEffect(MobEffects.STRENGTH,
|
||||
(int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)), 1, false, false));
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)),
|
||||
getProperty(EFFECT_STRENGTH).intValue() + bonusAmplifier));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,18 +47,18 @@ public class InvigoratingPresence extends Spell {
|
||||
for(int i = 0; i < 50 * modifiers.get(WizardryItems.blast_upgrade); i++){
|
||||
|
||||
double radius = (1 + world.rand.nextDouble() * 4) * modifiers.get(WizardryItems.blast_upgrade);
|
||||
double angle = world.rand.nextDouble() * Math.PI * 2;
|
||||
float angle = world.rand.nextFloat() * (float)Math.PI * 2;;
|
||||
|
||||
double x = caster.posX + radius * Math.cos(angle);
|
||||
double x = caster.posX + radius * MathHelper.cos(angle);
|
||||
double y = caster.getEntityBoundingBox().minY;
|
||||
double z = caster.posZ + radius * Math.sin(angle);
|
||||
double z = caster.posZ + radius * MathHelper.sin(angle);
|
||||
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.03, 0).time(50).clr(1, 0.2f, 0.2f).spawn(world);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1, 1 + 0.2f * world.rand.nextFloat());
|
||||
playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,26 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
public class InvokeWeather extends Spell {
|
||||
|
||||
public static final String THUNDERSTORM_CHANCE = "thunderstorm_chance";
|
||||
|
||||
public InvokeWeather(){
|
||||
super("invoke_weather", Tier.ADVANCED, Element.LIGHTNING, SpellType.UTILITY, 30, 100, EnumAction.BOW, false);
|
||||
super("invoke_weather", EnumAction.BOW, false);
|
||||
addProperties(THUNDERSTORM_CHANCE);
|
||||
soundValues(0.5f, 1, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -32,20 +33,21 @@ public class InvokeWeather extends Spell {
|
||||
int standardWeatherTime = (300 + (new Random()).nextInt(600)) * 20;
|
||||
|
||||
if(world.isRaining()){
|
||||
caster.sendMessage(new TextComponentTranslation("spell." + this.getUnlocalisedName() + ".sun"));
|
||||
caster.sendStatusMessage(new TextComponentTranslation("spell." + this.getUnlocalisedName() + ".sun"), true);
|
||||
world.getWorldInfo().setCleanWeatherTime(standardWeatherTime);
|
||||
world.getWorldInfo().setRainTime(0);
|
||||
world.getWorldInfo().setThunderTime(0);
|
||||
world.getWorldInfo().setRaining(false);
|
||||
world.getWorldInfo().setThundering(false);
|
||||
}else{
|
||||
caster.sendMessage(new TextComponentTranslation("spell." + this.getUnlocalisedName() + ".rain"));
|
||||
caster.sendStatusMessage(new TextComponentTranslation("spell." + this.getUnlocalisedName() + ".rain"), true);
|
||||
world.getWorldInfo().setCleanWeatherTime(0);
|
||||
world.getWorldInfo().setRainTime(standardWeatherTime);
|
||||
world.getWorldInfo().setThunderTime(standardWeatherTime);
|
||||
world.getWorldInfo().setRaining(true);
|
||||
// 1/3 chance for a thunderstorm
|
||||
world.getWorldInfo().setThundering(world.rand.nextInt(3) == 0);
|
||||
// Thunderstorm is guaranteed if the caster has a bottled thundercloud charm equipped
|
||||
world.getWorldInfo().setThundering(ItemArtefact.isArtefactActive(caster, WizardryItems.charm_storm)
|
||||
|| world.rand.nextFloat() < getProperty(THUNDERSTORM_CHANCE).floatValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +60,7 @@ public class InvokeWeather extends Spell {
|
||||
}
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_LIGHTNING_THUNDER, 0.5f, 1.0f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
@@ -14,8 +9,13 @@ import net.minecraft.world.World;
|
||||
|
||||
public class Leap extends Spell {
|
||||
|
||||
public static final String HORIZONTAL_SPEED = "horizontal_speed";
|
||||
public static final String VERTICAL_SPEED = "vertical_speed";
|
||||
|
||||
public Leap(){
|
||||
super("leap", Tier.BASIC, Element.EARTH, SpellType.UTILITY, 10, 20, EnumAction.NONE, false);
|
||||
super("leap", EnumAction.NONE, false);
|
||||
addProperties(HORIZONTAL_SPEED, VERTICAL_SPEED);
|
||||
soundValues(0.5f, 1, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -23,19 +23,20 @@ public class Leap extends Spell {
|
||||
|
||||
if(caster.onGround){
|
||||
|
||||
caster.motionY = 0.65 * modifiers.get(SpellModifiers.POTENCY);
|
||||
caster.addVelocity(caster.getLookVec().x * 0.3, 0, caster.getLookVec().z * 0.3);
|
||||
caster.motionY = getProperty(VERTICAL_SPEED).floatValue() * modifiers.get(SpellModifiers.POTENCY);
|
||||
double horizontalSpeed = getProperty(HORIZONTAL_SPEED).floatValue();
|
||||
caster.addVelocity(caster.getLookVec().x * horizontalSpeed, 0, caster.getLookVec().z * horizontalSpeed);
|
||||
|
||||
if(world.isRemote){
|
||||
for(int i = 0; i < 10; i++){
|
||||
double x = (double)(caster.posX + world.rand.nextFloat() - 0.5F);
|
||||
double y = (double)(caster.getEntityBoundingBox().minY);
|
||||
double z = (double)(caster.posZ + world.rand.nextFloat() - 0.5F);
|
||||
double x = caster.posX + world.rand.nextFloat() - 0.5F;
|
||||
double y = caster.getEntityBoundingBox().minY;
|
||||
double z = caster.posZ + world.rand.nextFloat() - 0.5F;
|
||||
world.spawnParticle(EnumParticleTypes.CLOUD, x, y, z, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERDRAGON_FLAP, 0.5F, 1.0f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
caster.swingArm(hand);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,30 +1,49 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Levitation extends Spell {
|
||||
|
||||
public static final String SPEED = "speed";
|
||||
public static final String ACCELERATION = "acceleration";
|
||||
|
||||
public Levitation(){
|
||||
super("levitation", Tier.ADVANCED, Element.SORCERY, SpellType.UTILITY, 10, 0, EnumAction.BOW, true);
|
||||
super("levitation", EnumAction.BOW, true);
|
||||
addProperties(SPEED, ACCELERATION);
|
||||
soundValues(0.5f, 1, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent[] createSounds(){
|
||||
return this.createContinuousSpellSounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, EntityLivingBase entity, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, entity, ticksInUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, double x, double y, double z, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, x, y, z, ticksInUse, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
caster.fallDistance = 0;
|
||||
if(!Wizardry.settings.replaceVanillaFallDamage) caster.fallDistance = 0;
|
||||
|
||||
caster.motionY = caster.motionY < 0.5 ? caster.motionY + 0.1 : caster.motionY;
|
||||
caster.motionY = caster.motionY < getProperty(SPEED).floatValue() ? caster.motionY
|
||||
+ getProperty(ACCELERATION).floatValue() : caster.motionY;
|
||||
|
||||
if(world.isRemote){
|
||||
double x = caster.posX - 0.25 + world.rand.nextDouble() * 0.5;
|
||||
@@ -32,9 +51,9 @@ public class Levitation extends Spell {
|
||||
double z = caster.posZ - 0.25 + world.rand.nextDouble() * 0.5;
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, -0.1, 0).time(15).clr(0.5f, 1, 0.7f).spawn(world);
|
||||
}
|
||||
if(ticksInUse % 24 == 0 && world.isRemote){
|
||||
Wizardry.proxy.playMovingSound(caster, WizardrySounds.SPELL_LOOP_SPARKLE, 0.5f, 1, false);
|
||||
}
|
||||
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -11,79 +7,67 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class LifeDrain extends SpellRay {
|
||||
|
||||
private static final float BASE_DAMAGE = 2;
|
||||
/** The fraction of the damage dealt that is transferred to the caster as healing. */
|
||||
private static final float HEAL_FACTOR = 0.35f;
|
||||
|
||||
public static final String HEAL_FACTOR = "heal_factor";
|
||||
|
||||
public LifeDrain(){
|
||||
super("life_drain", Tier.APPRENTICE, Element.NECROMANCY, SpellType.ATTACK, 10, 0, true, 10, null);
|
||||
super("life_drain", true, EnumAction.NONE);
|
||||
this.particleVelocity(-0.5);
|
||||
this.particleSpacing(0.4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
// TODO: Temporary solution until I implement a better continuous sound system
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, modifiers);
|
||||
if(flag){
|
||||
if(ticksInUse % 18 == 0){
|
||||
if(ticksInUse == 0) WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SUMMONING, 1, 0.6f);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_CRACKLE, 2, 1);
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
addProperties(DAMAGE, HEAL_FACTOR);
|
||||
this.soundValues(0.6f, 1, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, target, modifiers);
|
||||
if(flag){
|
||||
if(ticksInUse % 18 == 0){
|
||||
if(ticksInUse == 0) caster.playSound(WizardrySounds.SPELL_SUMMONING, 1, 0.6f);
|
||||
caster.playSound(WizardrySounds.SPELL_LOOP_CRACKLE, 2, 1);
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
protected SoundEvent[] createSounds(){
|
||||
return this.createContinuousSpellSounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected void playSound(World world, EntityLivingBase entity, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, entity, ticksInUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, double x, double y, double z, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, x, y, z, ticksInUse, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
if(ticksInUse % 12 == 0){
|
||||
|
||||
float damage = BASE_DAMAGE * modifiers.get(SpellModifiers.POTENCY);
|
||||
float damage = getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY);
|
||||
|
||||
WizardryUtilities.attackEntityWithoutKnockback(target, MagicDamage.causeDirectMagicDamage(caster,
|
||||
DamageType.MAGIC), damage);
|
||||
|
||||
caster.heal(damage * HEAL_FACTOR);
|
||||
if(caster != null) caster.heal(damage * getProperty(HEAL_FACTOR).floatValue());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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 onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.tileentity.TileEntityTimer;
|
||||
import electroblob.wizardry.util.RayTracer;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
@@ -19,18 +16,21 @@ import net.minecraft.world.World;
|
||||
public class Light extends Spell {
|
||||
|
||||
public Light(){
|
||||
super("light", Tier.BASIC, Element.SORCERY, SpellType.UTILITY, 5, 15, EnumAction.NONE, false);
|
||||
super("light", EnumAction.NONE, false);
|
||||
addProperties(RANGE, DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesSpellRequirePacket(){
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
RayTraceResult rayTrace = WizardryUtilities.standardBlockRayTrace(world, caster, 4, false);
|
||||
double range = getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade);
|
||||
|
||||
RayTraceResult rayTrace = RayTracer.standardBlockRayTrace(world, caster, range, false);
|
||||
|
||||
if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){
|
||||
|
||||
@@ -41,20 +41,21 @@ public class Light extends Spell {
|
||||
if(!world.isRemote){
|
||||
world.setBlockState(pos, WizardryBlocks.magic_light.getDefaultState());
|
||||
if(world.getTileEntity(pos) instanceof TileEntityTimer){
|
||||
((TileEntityTimer)world.getTileEntity(pos))
|
||||
.setLifetime((int)(600 * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
int lifetime = ItemArtefact.isArtefactActive(caster, WizardryItems.charm_light) ? -1
|
||||
: (int)(getProperty(DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade));
|
||||
((TileEntityTimer)world.getTileEntity(pos)).setLifetime(lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
caster.swingArm(hand);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
}else{
|
||||
|
||||
int x = (int)(Math.floor(caster.posX) + caster.getLookVec().x * 4);
|
||||
int y = (int)(Math.floor(caster.posY) + caster.eyeHeight + caster.getLookVec().y * 4);
|
||||
int z = (int)(Math.floor(caster.posZ) + caster.getLookVec().z * 4);
|
||||
int x = (int)(Math.floor(caster.posX) + caster.getLookVec().x * range);
|
||||
int y = (int)(Math.floor(caster.posY) + caster.eyeHeight + caster.getLookVec().y * range);
|
||||
int z = (int)(Math.floor(caster.posZ) + caster.getLookVec().z * range);
|
||||
|
||||
BlockPos pos = new BlockPos(x, y, z);
|
||||
|
||||
@@ -62,12 +63,13 @@ public class Light extends Spell {
|
||||
if(!world.isRemote){
|
||||
world.setBlockState(pos, WizardryBlocks.magic_light.getDefaultState());
|
||||
if(world.getTileEntity(pos) instanceof TileEntityTimer){
|
||||
((TileEntityTimer)world.getTileEntity(pos))
|
||||
.setLifetime((int)(600 * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
int lifetime = ItemArtefact.isArtefactActive(caster, WizardryItems.charm_light) ? -1
|
||||
: (int)(getProperty(DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade));
|
||||
((TileEntityTimer)world.getTileEntity(pos)).setLifetime(lifetime);
|
||||
}
|
||||
}
|
||||
caster.swingArm(hand);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +1,51 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.effect.EntityLightningBolt;
|
||||
import net.minecraft.entity.monster.EntityCreeper;
|
||||
import net.minecraft.entity.passive.EntityPig;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.EntityStruckByLightningEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
//@Mod.EventBusSubscriber
|
||||
public class LightningBolt extends SpellRay {
|
||||
|
||||
/** The NBT key used to store the UUID of the player that summoned the lightning bolt. Used for achievements. */
|
||||
public static final String NBT_KEY = "summoningPlayer";
|
||||
|
||||
public LightningBolt(){
|
||||
super("lightning_bolt", Tier.ADVANCED, Element.LIGHTNING, SpellType.ATTACK, 40, 80, false, 200, null);
|
||||
this.ignoreEntities(true);
|
||||
super("lightning_bolt", false, EnumAction.NONE);
|
||||
this.ignoreLivingEntities(true);
|
||||
}
|
||||
|
||||
@Override public boolean doesSpellRequirePacket(){ return false; }
|
||||
@Override public boolean requiresPacket(){ return false; }
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(world.canBlockSeeSky(pos.up())){
|
||||
|
||||
if(!world.isRemote){
|
||||
// Temporarily disable the fire tick gamerule if player block damage is disabled
|
||||
// Bit of a hack but it works fine!
|
||||
boolean doFireTick = world.getGameRules().getBoolean("doFireTick");
|
||||
if(doFireTick && !Wizardry.settings.playerBlockDamage) world.getGameRules().setOrCreateGameRule("doFireTick", "false");
|
||||
EntityLightningBolt entitylightning = new EntityLightningBolt(world, pos.getX(), pos.getY(),
|
||||
pos.getZ(), false);
|
||||
world.addWeatherEffect(entitylightning);
|
||||
// Reset doFireTick to true if it was true before
|
||||
if(doFireTick && !Wizardry.settings.playerBlockDamage) world.getGameRules().setOrCreateGameRule("doFireTick", "true");
|
||||
|
||||
// Code for eventhandler recognition for achievements
|
||||
if(caster instanceof EntityPlayer){
|
||||
@@ -62,26 +61,26 @@ public class LightningBolt extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onEntityStruckByLightningEvent(EntityStruckByLightningEvent event){
|
||||
|
||||
if(event.getLightning().getEntityData() != null && event.getLightning().getEntityData().hasUniqueId(NBT_KEY)){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)WizardryUtilities.getEntityByUUID(event.getLightning().world,
|
||||
event.getLightning().getEntityData().getUniqueId("summoningPlayer"));
|
||||
|
||||
if(event.getEntity() instanceof EntityCreeper){
|
||||
WizardryAdvancementTriggers.charge_creeper.triggerFor(player);
|
||||
}
|
||||
|
||||
if(event.getEntity() instanceof EntityPig){
|
||||
WizardryAdvancementTriggers.frankenstein.triggerFor(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
// @SubscribeEvent
|
||||
// public static void onEntityStruckByLightningEvent(EntityStruckByLightningEvent event){
|
||||
//
|
||||
// if(event.getLightning().getEntityData() != null && event.getLightning().getEntityData().hasUniqueId(NBT_KEY)){
|
||||
//
|
||||
// EntityPlayer player = (EntityPlayer)WizardryUtilities.getEntityByUUID(event.getLightning().world,
|
||||
// event.getLightning().getEntityData().getUniqueId("summoningPlayer"));
|
||||
//
|
||||
// if(event.getEntity() instanceof EntityCreeper){
|
||||
// WizardryAdvancementTriggers.charge_creeper.triggerFor(player);
|
||||
// }
|
||||
//
|
||||
// if(event.getEntity() instanceof EntityPig){
|
||||
// WizardryAdvancementTriggers.frankenstein.triggerFor(player);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
@@ -1,33 +1,33 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.construct.EntityHammer;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class LightningHammer extends SpellConstructRanged<EntityHammer> {
|
||||
|
||||
public static final String ATTACK_INTERVAL = "attack_interval";
|
||||
public static final String SECONDARY_MAX_TARGETS = "secondary_max_targets";
|
||||
|
||||
public LightningHammer(){
|
||||
super("lightning_hammer", Tier.MASTER, Element.LIGHTNING, SpellType.ATTACK, 100, 300, EntityHammer::new, 600, 40, WizardrySounds.SPELL_SUMMONING);
|
||||
super("lightning_hammer", EntityHammer::new, false);
|
||||
this.soundValues(3, 1, 0);
|
||||
this.floor(true);
|
||||
this.overlap(true);
|
||||
addProperties(EFFECT_RADIUS, SECONDARY_MAX_TARGETS, ATTACK_INTERVAL, DIRECT_DAMAGE, SPLASH_DAMAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean spawnConstruct(World world, double x, double y, double z, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
// TESTME: Does this need to be one block higher?
|
||||
protected boolean spawnConstruct(World world, double x, double y, double z, EnumFacing side, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
if(!world.canBlockSeeSky(new BlockPos(x, y, z))) return false;
|
||||
return super.spawnConstruct(world, x, y + 50, z, caster, modifiers);
|
||||
return super.spawnConstruct(world, x, y + 50, z, side, caster, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addConstructExtras(EntityHammer construct, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
protected void addConstructExtras(EntityHammer construct, EnumFacing side, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
construct.motionY = -2;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,56 +1,52 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.*;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.network.play.server.SPacketEntityVelocity;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class LightningPulse extends Spell {
|
||||
|
||||
private static final double BASE_RADIUS = 3;
|
||||
private static final float BASE_DAMAGE = 8;
|
||||
|
||||
public static final String REPULSION_VELOCITY = "repulsion_velocity";
|
||||
|
||||
public LightningPulse(){
|
||||
super("lightning_pulse", Tier.ADVANCED, Element.LIGHTNING, SpellType.ATTACK, 25, 75, EnumAction.NONE, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesSpellRequirePacket(){
|
||||
return false;
|
||||
super("lightning_pulse", EnumAction.NONE, false);
|
||||
addProperties(EFFECT_RADIUS, DAMAGE, REPULSION_VELOCITY);
|
||||
this.soundValues(2, 1, 0);
|
||||
}
|
||||
|
||||
// TODO: NPC casting support
|
||||
|
||||
@Override
|
||||
protected SoundEvent[] createSounds(){
|
||||
return createSoundsWithSuffixes("spark", "explosion");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(caster.onGround){
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(
|
||||
BASE_RADIUS * modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world);
|
||||
getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade),
|
||||
caster.posX, caster.posY, caster.posZ, world);
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
if(WizardryUtilities.isValidTarget(caster, target)){
|
||||
if(AllyDesignationSystem.isValidTarget(caster, target)){
|
||||
// Base damage is 4 hearts no matter where the target is.
|
||||
target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK),
|
||||
BASE_DAMAGE * modifiers.get(SpellModifiers.POTENCY));
|
||||
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
@@ -61,9 +57,9 @@ public class LightningPulse extends Spell {
|
||||
dx /= vectorLength;
|
||||
dz /= vectorLength;
|
||||
|
||||
target.motionX = 0.8 * dx;
|
||||
target.motionX = getProperty(REPULSION_VELOCITY).floatValue() * dx;
|
||||
target.motionY = 0;
|
||||
target.motionZ = 0.8 * dz;
|
||||
target.motionZ = getProperty(REPULSION_VELOCITY).floatValue() * dz;
|
||||
|
||||
// Player motion is handled on that player's client so needs packets
|
||||
if(target instanceof EntityPlayerMP){
|
||||
@@ -80,8 +76,7 @@ public class LightningPulse extends Spell {
|
||||
}
|
||||
|
||||
caster.swingArm(hand);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1, 1);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SHOCKWAVE, 2, 1);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -11,100 +7,90 @@ import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class LightningRay extends SpellRay {
|
||||
|
||||
private static final float BASE_DAMAGE = 3;
|
||||
|
||||
public LightningRay(){
|
||||
super("lightning_ray", Tier.APPRENTICE, Element.LIGHTNING, SpellType.ATTACK, 5, 0, true, 10, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
// TODO: Temporary solution until I implement a better continuous sound system
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, modifiers);
|
||||
if(flag){
|
||||
if(ticksInUse == 1){
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1, 1);
|
||||
}else if(ticksInUse > 0 && ticksInUse % 20 == 0){
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_LIGHTNING, 1, 1);
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
super("lightning_ray", true, EnumAction.NONE);
|
||||
this.aimAssist(0.6f);
|
||||
addProperties(DAMAGE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, target, modifiers);
|
||||
if(flag){
|
||||
if(ticksInUse == 1){
|
||||
caster.playSound(WizardrySounds.SPELL_LIGHTNING, 1, 1);
|
||||
}else if(ticksInUse > 0 && ticksInUse % 20 == 0){
|
||||
caster.playSound(WizardrySounds.SPELL_LOOP_LIGHTNING, 1, 1);
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
protected SoundEvent[] createSounds(){
|
||||
return this.createContinuousSpellSounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
protected void playSound(World world, EntityLivingBase entity, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, entity, ticksInUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, double x, double y, double z, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, x, y, z, ticksInUse, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
if(MagicDamage.isEntityImmune(DamageType.SHOCK, target)){
|
||||
if(!world.isRemote && ticksInUse == 1 && caster instanceof EntityPlayer)
|
||||
((EntityPlayer)caster).sendStatusMessage(new TextComponentTranslation("spell.resist", target.getName(),
|
||||
this.getNameForTranslationFormatted()), true);
|
||||
}else{
|
||||
// This now only damages in line with the maxHurtResistantTime. Some mods don't play nicely and fiddle
|
||||
// with this mechanic for their own purposes, so this line makes sure that doesn't affect wizardry.
|
||||
}else if(ticksInUse % ((EntityLivingBase)target).maxHurtResistantTime == 1){
|
||||
WizardryUtilities.attackEntityWithoutKnockback(target,
|
||||
MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK),
|
||||
BASE_DAMAGE * modifiers.get(SpellModifiers.POTENCY));
|
||||
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
}
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
|
||||
if(ticksInUse % 3 == 0) ParticleBuilder.create(Type.LIGHTNING).entity(caster)
|
||||
.pos(caster != null ? origin.subtract(caster.getPositionVector()) : origin).target(target).spawn(world);
|
||||
|
||||
|
||||
// Particle effect
|
||||
for(int i=0; i<5; i++){
|
||||
ParticleBuilder.create(Type.SPARK, target).spawn(world);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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 onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
// This is a nice example of when onMiss is used for more than just returning a boolean
|
||||
if(world.isRemote && ticksInUse % 4 == 0){
|
||||
|
||||
if(caster != null) origin = origin.subtract(0, Y_OFFSET, 0);
|
||||
|
||||
double freeRange = 0.8 * baseRange; // The arc does not reach full range when it has a free end
|
||||
Vec3d endpoint = origin.add(direction.scale(freeRange));
|
||||
|
||||
ParticleBuilder.create(Type.LIGHTNING).entity(caster)
|
||||
.pos(caster != null ? origin.subtract(caster.getPositionVector()) : origin).target(endpoint).spawn(world);
|
||||
|
||||
// The arc does not reach full range when it has a free end
|
||||
double freeRange = 0.8 * getRange(world, origin, direction, caster, ticksInUse, modifiers);
|
||||
|
||||
if(caster != null){
|
||||
ParticleBuilder.create(Type.LIGHTNING).entity(caster).pos(origin.subtract(caster.getPositionVector()))
|
||||
.length(freeRange).spawn(world);
|
||||
}else{
|
||||
ParticleBuilder.create(Type.LIGHTNING).pos(origin).target(origin.add(direction.scale(freeRange))).spawn(world);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
@@ -1,146 +1,138 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.*;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class LightningWeb extends SpellRay {
|
||||
|
||||
private static final float PRIMARY_DAMAGE = 5;
|
||||
private static final float SECONDARY_DAMAGE = 4;
|
||||
private static final float TERTIARY_DAMAGE = 3;
|
||||
import java.util.List;
|
||||
|
||||
private static final double PRIMARY_RANGE = 10;
|
||||
private static final double SECONDARY_RANGE = 5;
|
||||
private static final double TERTIARY_RANGE = 5;
|
||||
|
||||
private static final int SECONDARY_MAX_TARGETS = 5;
|
||||
private static final int TERTIARY_MAX_TARGETS = 2; // This is per secondary target, giving 10 in total
|
||||
public class LightningWeb extends SpellRay {
|
||||
|
||||
public static final String PRIMARY_DAMAGE = "primary_damage";
|
||||
public static final String SECONDARY_DAMAGE = "secondary_damage";
|
||||
public static final String TERTIARY_DAMAGE = "tertiary_damage";
|
||||
|
||||
public static final String SECONDARY_RANGE = "secondary_range";
|
||||
public static final String TERTIARY_RANGE = "tertiary_range";
|
||||
|
||||
public static final String SECONDARY_MAX_TARGETS = "secondary_max_targets";
|
||||
public static final String TERTIARY_MAX_TARGETS = "tertiary_max_targets"; // This is per secondary target
|
||||
|
||||
public LightningWeb(){
|
||||
super("lightning_web", Tier.MASTER, Element.LIGHTNING, SpellType.ATTACK, 15, 0, true, PRIMARY_RANGE, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
// TODO: Temporary solution until I implement a better continuous sound system
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, modifiers);
|
||||
if(flag){
|
||||
if(ticksInUse == 1){
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LIGHTNING, 1, 1);
|
||||
}else if(ticksInUse > 0 && ticksInUse % 20 == 0){
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_LOOP_LIGHTNING, 1, 1);
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
super("lightning_web", true, EnumAction.NONE);
|
||||
this.aimAssist(0.6f);
|
||||
addProperties(PRIMARY_DAMAGE, SECONDARY_DAMAGE, TERTIARY_DAMAGE, SECONDARY_RANGE, TERTIARY_RANGE,
|
||||
SECONDARY_MAX_TARGETS, TERTIARY_MAX_TARGETS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, target, modifiers);
|
||||
if(flag){
|
||||
if(ticksInUse == 1){
|
||||
caster.playSound(WizardrySounds.SPELL_LIGHTNING, 1, 1);
|
||||
}else if(ticksInUse > 0 && ticksInUse % 20 == 0){
|
||||
caster.playSound(WizardrySounds.SPELL_LOOP_LIGHTNING, 1, 1);
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
protected SoundEvent[] createSounds(){
|
||||
return this.createContinuousSpellSounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, EntityLivingBase entity, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, entity, ticksInUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, double x, double y, double z, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, x, y, z, ticksInUse, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
electrocute(world, caster, caster, target, PRIMARY_DAMAGE * modifiers.get(SpellModifiers.POTENCY), ticksInUse);
|
||||
|
||||
electrocute(world, caster, origin, target, getProperty(PRIMARY_DAMAGE).floatValue()
|
||||
* modifiers.get(SpellModifiers.POTENCY), ticksInUse);
|
||||
|
||||
// Secondary chaining effect
|
||||
|
||||
List<EntityLivingBase> secondaryTargets = WizardryUtilities.getEntitiesWithinRadius(SECONDARY_RANGE,
|
||||
target.posX, target.posY + target.height / 2, target.posZ, world);
|
||||
List<EntityLivingBase> secondaryTargets = WizardryUtilities.getEntitiesWithinRadius(
|
||||
getProperty(SECONDARY_RANGE).floatValue(), target.posX, target.posY + target.height / 2,
|
||||
target.posZ, world);
|
||||
|
||||
secondaryTargets.remove(target);
|
||||
secondaryTargets.removeIf(e -> !WizardryUtilities.isLiving(e));
|
||||
secondaryTargets.removeIf(e -> !WizardryUtilities.isValidTarget(caster, e));
|
||||
if(secondaryTargets.size() > SECONDARY_MAX_TARGETS) secondaryTargets = secondaryTargets.subList(0, SECONDARY_MAX_TARGETS);
|
||||
secondaryTargets.removeIf(e -> !AllyDesignationSystem.isValidTarget(caster, e));
|
||||
if(secondaryTargets.size() > getProperty(SECONDARY_MAX_TARGETS).intValue())
|
||||
secondaryTargets = secondaryTargets.subList(0, getProperty(SECONDARY_MAX_TARGETS).intValue());
|
||||
|
||||
for(EntityLivingBase secondaryTarget : secondaryTargets){
|
||||
|
||||
electrocute(world, caster, target, secondaryTarget,
|
||||
SECONDARY_DAMAGE * modifiers.get(SpellModifiers.POTENCY), ticksInUse);
|
||||
electrocute(world, caster, target.getPositionVector().add(0, target.height/2, 0), secondaryTarget,
|
||||
getProperty(SECONDARY_DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY), ticksInUse);
|
||||
|
||||
// Tertiary chaining effect
|
||||
|
||||
List<EntityLivingBase> tertiaryTargets = WizardryUtilities.getEntitiesWithinRadius(TERTIARY_RANGE,
|
||||
secondaryTarget.posX, secondaryTarget.posY + secondaryTarget.height / 2,
|
||||
secondaryTarget.posZ, world);
|
||||
List<EntityLivingBase> tertiaryTargets = WizardryUtilities.getEntitiesWithinRadius(
|
||||
getProperty(TERTIARY_RANGE).floatValue(), secondaryTarget.posX,
|
||||
secondaryTarget.posY + secondaryTarget.height / 2, secondaryTarget.posZ, world);
|
||||
|
||||
tertiaryTargets.remove(target);
|
||||
tertiaryTargets.removeAll(secondaryTargets);
|
||||
tertiaryTargets.removeIf(e -> !WizardryUtilities.isLiving(e));
|
||||
tertiaryTargets.removeIf(e -> !WizardryUtilities.isValidTarget(caster, e));
|
||||
if(tertiaryTargets.size() > TERTIARY_MAX_TARGETS) tertiaryTargets = tertiaryTargets.subList(0, TERTIARY_MAX_TARGETS);
|
||||
tertiaryTargets.removeIf(e -> !AllyDesignationSystem.isValidTarget(caster, e));
|
||||
if(tertiaryTargets.size() > getProperty(TERTIARY_MAX_TARGETS).intValue())
|
||||
tertiaryTargets = tertiaryTargets.subList(0, getProperty(TERTIARY_MAX_TARGETS).intValue());
|
||||
|
||||
for(EntityLivingBase tertiaryTarget : tertiaryTargets){
|
||||
electrocute(world, caster, secondaryTarget, tertiaryTarget,
|
||||
TERTIARY_DAMAGE * modifiers.get(SpellModifiers.POTENCY), ticksInUse);
|
||||
electrocute(world, caster, secondaryTarget.getPositionVector().add(0, secondaryTarget.height/2, 0),
|
||||
tertiaryTarget, getProperty(TERTIARY_DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY), ticksInUse);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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 onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
// This is a nice example of when onMiss is used for more than just returning a boolean
|
||||
if(world.isRemote){
|
||||
|
||||
if(caster != null) origin = origin.subtract(0, Y_OFFSET, 0);
|
||||
|
||||
double freeRange = 0.8 * baseRange; // The arc does not reach full range when it has a free end
|
||||
Vec3d endpoint = origin.add(direction.scale(freeRange));
|
||||
|
||||
ParticleBuilder.create(Type.BEAM).entity(caster).clr(0.2f, 0.6f, 1)
|
||||
.pos(caster != null ? origin.subtract(caster.getPositionVector()) : origin).target(endpoint).spawn(world);
|
||||
|
||||
if(ticksInUse % 2 == 0){
|
||||
|
||||
ParticleBuilder.create(Type.LIGHTNING).entity(caster)
|
||||
.pos(caster != null ? origin.subtract(caster.getPositionVector()) : origin).target(endpoint).spawn(world);
|
||||
|
||||
// The arc does not reach full range when it has a free end
|
||||
double freeRange = 0.8 * getRange(world, origin, direction, caster, ticksInUse, modifiers);
|
||||
|
||||
if(caster != null){
|
||||
ParticleBuilder.create(Type.BEAM).entity(caster).pos(origin.subtract(caster.getPositionVector()))
|
||||
.length(freeRange).clr(0.2f, 0.6f, 1).spawn(world);
|
||||
}else{
|
||||
ParticleBuilder.create(Type.BEAM).pos(origin).target(origin.add(direction.scale(freeRange)))
|
||||
.clr(0.2f, 0.6f, 1).spawn(world);
|
||||
}
|
||||
|
||||
if(ticksInUse % 4 == 0){
|
||||
if(caster != null){
|
||||
ParticleBuilder.create(Type.LIGHTNING).entity(caster).pos(origin.subtract(caster.getPositionVector()))
|
||||
.length(freeRange).spawn(world);
|
||||
}else{
|
||||
ParticleBuilder.create(Type.LIGHTNING).pos(origin).target(origin.add(direction.scale(freeRange))).spawn(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void electrocute(World world, Entity caster, Entity origin, Entity target, float damage, int ticksInUse){
|
||||
private void electrocute(World world, Entity caster, Vec3d origin, Entity target, float damage, int ticksInUse){
|
||||
|
||||
if(MagicDamage.isEntityImmune(DamageType.SHOCK, target)){
|
||||
if(!world.isRemote && ticksInUse == 1 && caster instanceof EntityPlayer)
|
||||
|
||||
@@ -2,35 +2,18 @@ package electroblob.wizardry.spell;
|
||||
|
||||
import com.google.common.collect.BiMap;
|
||||
import com.google.common.collect.HashBiMap;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.living.EntitySkeletonMinion;
|
||||
import electroblob.wizardry.entity.living.EntityWitherSkeletonMinion;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.entity.living.*;
|
||||
import electroblob.wizardry.util.NBTExtras;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.monster.EntityCaveSpider;
|
||||
import net.minecraft.entity.monster.EntityHusk;
|
||||
import net.minecraft.entity.monster.EntityMagmaCube;
|
||||
import net.minecraft.entity.monster.EntityPigZombie;
|
||||
import net.minecraft.entity.monster.EntitySkeleton;
|
||||
import net.minecraft.entity.monster.EntitySlime;
|
||||
import net.minecraft.entity.monster.EntitySpider;
|
||||
import net.minecraft.entity.monster.EntityStray;
|
||||
import net.minecraft.entity.monster.EntityWitherSkeleton;
|
||||
import net.minecraft.entity.monster.EntityZombie;
|
||||
import net.minecraft.entity.passive.EntityBat;
|
||||
import net.minecraft.entity.passive.EntityChicken;
|
||||
import net.minecraft.entity.passive.EntityCow;
|
||||
import net.minecraft.entity.passive.EntityMooshroom;
|
||||
import net.minecraft.entity.passive.EntityPig;
|
||||
import net.minecraft.entity.monster.*;
|
||||
import net.minecraft.entity.passive.*;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -46,10 +29,11 @@ public class Metamorphosis extends SpellRay {
|
||||
addTransformation(EntityCow.class, EntityMooshroom.class);
|
||||
addTransformation(EntityChicken.class, EntityBat.class);
|
||||
addTransformation(EntityZombie.class, EntityHusk.class);
|
||||
addTransformation(EntitySkeleton.class, EntityWitherSkeleton.class, EntityStray.class);
|
||||
addTransformation(EntitySkeleton.class, EntityStray.class, EntityWitherSkeleton.class);
|
||||
addTransformation(EntitySpider.class, EntityCaveSpider.class);
|
||||
addTransformation(EntitySlime.class, EntityMagmaCube.class);
|
||||
addTransformation(EntitySkeletonMinion.class, EntityWitherSkeletonMinion.class);
|
||||
addTransformation(EntityZombieMinion.class, EntityHuskMinion.class);
|
||||
addTransformation(EntitySkeletonMinion.class, EntityStrayMinion.class, EntityWitherSkeletonMinion.class);
|
||||
}
|
||||
|
||||
/** Adds circular mappings between the given entity classes to the transformations map. In other words, given an
|
||||
@@ -64,23 +48,24 @@ public class Metamorphosis extends SpellRay {
|
||||
}
|
||||
|
||||
public Metamorphosis(){
|
||||
super("metamorphosis", Tier.APPRENTICE, Element.NECROMANCY, SpellType.UTILITY, 15, 30, false, 10, WizardrySounds.SPELL_DEFLECTION);
|
||||
this.soundValues(0.5f, 0.8f, 0);
|
||||
super("metamorphosis", false, EnumAction.NONE);
|
||||
this.soundValues(0.5f, 1f, 0);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs() { return false; }
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
|
||||
double xPos = target.posX;
|
||||
double yPos = target.posY;
|
||||
double zPos = target.posZ;
|
||||
|
||||
// Sneaking allows the entities to be cycled through in the other direction.
|
||||
Class<? extends EntityLivingBase> newEntityClass = caster.isSneaking() ?
|
||||
// Dispensers always cycle through entities in the normal direction.
|
||||
Class<? extends EntityLivingBase> newEntityClass = caster != null && caster.isSneaking() ?
|
||||
TRANSFORMATIONS.inverse().get(target.getClass()) : TRANSFORMATIONS.get(target.getClass());
|
||||
|
||||
if(newEntityClass == null) return false;
|
||||
@@ -103,7 +88,7 @@ public class Metamorphosis extends SpellRay {
|
||||
NBTTagCompound tag = new NBTTagCompound();
|
||||
target.writeToNBT(tag);
|
||||
// Remove the UUID because keeping it the same causes the entity to disappear
|
||||
WizardryUtilities.removeUniqueId(tag, "UUID");
|
||||
NBTExtras.removeUniqueId(tag, "UUID");
|
||||
newEntity.readFromNBT(tag);
|
||||
|
||||
target.setDead();
|
||||
@@ -111,22 +96,27 @@ public class Metamorphosis extends SpellRay {
|
||||
world.spawnEntity(newEntity);
|
||||
|
||||
}else{
|
||||
for(int i=0; i<5; i++){
|
||||
ParticleBuilder.create(Type.DARK_MAGIC).pos(xPos, yPos, zPos).clr(0.1f, 0, 0).spawn(world);
|
||||
for(int i=0; i<20; i++){
|
||||
ParticleBuilder.create(Type.DARK_MAGIC, world.rand, xPos, yPos + 1, zPos, 1, false)
|
||||
.clr(0.1f, 0, 0).spawn(world);
|
||||
}
|
||||
ParticleBuilder.create(Type.BUFF).pos(xPos, yPos, zPos).clr(0xd363cb).spawn(world);
|
||||
}
|
||||
|
||||
this.playSound(world, (EntityLivingBase)target, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
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, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,41 +1,44 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.EntityMeteor;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Meteor extends SpellRay {
|
||||
|
||||
// It doesn't really make sense to have a blast radius when the explosion is measured by strength
|
||||
public static final String BLAST_STRENGTH = "blast_strength";
|
||||
|
||||
public Meteor(){
|
||||
super("meteor", Tier.MASTER, Element.FIRE, SpellType.ATTACK, 100, 200, false, 40, WizardrySounds.SPELL_SUMMONING);
|
||||
super("meteor", false, EnumAction.NONE);
|
||||
this.soundValues(3, 1, 0);
|
||||
this.ignoreEntities(true);
|
||||
this.ignoreLivingEntities(true);
|
||||
addProperties(BLAST_STRENGTH);
|
||||
}
|
||||
|
||||
@Override public boolean doesSpellRequirePacket(){ return false; }
|
||||
@Override public boolean requiresPacket(){ return false; }
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(world.canBlockSeeSky(pos.up())){
|
||||
|
||||
if(!world.isRemote){
|
||||
EntityMeteor meteor = new EntityMeteor(world, pos.getX(), pos.getY() + 50, pos.getZ(),
|
||||
modifiers.get(WizardryItems.blast_upgrade));
|
||||
modifiers.get(WizardryItems.blast_upgrade), WizardryUtilities.canDamageBlocks(caster, world));
|
||||
world.spawnEntity(meteor);
|
||||
}
|
||||
|
||||
@@ -46,7 +49,7 @@ public class Meteor extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +1,27 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import java.util.Arrays;
|
||||
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.AllyDesignationSystem;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityList;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.INpc;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
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.EnumAction;
|
||||
import net.minecraft.item.EnumDyeColor;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
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;
|
||||
@@ -33,45 +29,57 @@ import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
|
||||
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";
|
||||
|
||||
private static final int BASE_DURATION = 600;
|
||||
|
||||
public MindControl(){
|
||||
super("mind_control", Tier.ADVANCED, Element.NECROMANCY, SpellType.ATTACK, 40, 150, false, 8, WizardrySounds.SPELL_SUMMONING);
|
||||
super("mind_control", false, EnumAction.NONE);
|
||||
addProperties(EFFECT_DURATION);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs() { return false; }
|
||||
@Override public boolean canBeCastByDispensers() { return false; }
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
if(!canControl(target)){
|
||||
// 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(!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);
|
||||
}
|
||||
|
||||
NBTTagCompound entityNBT = target.getEntityData();
|
||||
if(entityNBT != null) entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID());
|
||||
|
||||
((EntityLiving)target).addPotionEffect(new PotionEffect(WizardryPotions.mind_control,
|
||||
(int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)), 0));
|
||||
}
|
||||
|
||||
}else{
|
||||
|
||||
if(target instanceof EntitySheep && ((EntitySheep)target).getFleeceColor() == EnumDyeColor.BLUE
|
||||
&& WizardryUtilities.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,
|
||||
@@ -90,12 +98,12 @@ public class MindControl extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
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, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -106,6 +114,11 @@ public class MindControl extends SpellRay {
|
||||
.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
|
||||
@@ -122,7 +135,7 @@ public class MindControl extends SpellRay {
|
||||
// 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.getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE).getAttributeValue(),
|
||||
target.posX, target.posY, target.posZ, world);
|
||||
|
||||
possibleTargets.remove(target);
|
||||
@@ -131,7 +144,7 @@ public class MindControl extends SpellRay {
|
||||
EntityLivingBase newAITarget = null;
|
||||
|
||||
for(EntityLivingBase possibleTarget : possibleTargets){
|
||||
if(WizardryUtilities.isValidTarget(caster, possibleTarget) && (newAITarget == null
|
||||
if(AllyDesignationSystem.isValidTarget(caster, possibleTarget) && (newAITarget == null
|
||||
|| target.getDistance(possibleTarget) < target.getDistance(newAITarget))){
|
||||
newAITarget = possibleTarget;
|
||||
}
|
||||
@@ -140,7 +153,7 @@ public class MindControl extends SpellRay {
|
||||
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);
|
||||
target.setAttackTarget(newAITarget);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -163,7 +176,7 @@ public class MindControl extends SpellRay {
|
||||
if(caster instanceof EntityLivingBase){
|
||||
|
||||
// If the current target is already a valid mind control target, nothing happens.
|
||||
if(WizardryUtilities.isValidTarget(caster, currentTarget)) return;
|
||||
if(AllyDesignationSystem.isValidTarget(caster, currentTarget)) return;
|
||||
|
||||
if(MindControl.findMindControlTarget(entity, (EntityLivingBase)caster, world)){
|
||||
// If it worked, skip setting the target to null.
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
@@ -15,9 +11,11 @@ import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
|
||||
@@ -26,16 +24,15 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class MindTrick extends SpellRay {
|
||||
|
||||
private static final int BASE_DURATION = 300;
|
||||
|
||||
public MindTrick(){
|
||||
super("mind_trick", Tier.BASIC, Element.NECROMANCY, SpellType.ATTACK, 10, 40, false, 8, WizardrySounds.SPELL_DEFLECTION);
|
||||
super("mind_trick", false, EnumAction.NONE);
|
||||
this.soundValues(0.7f, 1, 0.4f);
|
||||
addProperties(EFFECT_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
@@ -44,13 +41,13 @@ public class MindTrick extends SpellRay {
|
||||
if(target instanceof EntityPlayer){
|
||||
|
||||
((EntityLivingBase)target).addPotionEffect(new PotionEffect(MobEffects.NAUSEA,
|
||||
(int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)), 0));
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), 0));
|
||||
|
||||
}else if(target instanceof EntityLiving){
|
||||
|
||||
((EntityLiving)target).setAttackTarget(null);
|
||||
((EntityLivingBase)target).addPotionEffect(new PotionEffect(WizardryPotions.mind_trick,
|
||||
(int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)), 0));
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), 0));
|
||||
}
|
||||
|
||||
}else{
|
||||
@@ -68,12 +65,12 @@ public class MindTrick extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
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, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.ForgeHooks;
|
||||
import net.minecraftforge.event.ForgeEventFactory;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Mine extends SpellRay {
|
||||
|
||||
public Mine(){
|
||||
super("mine", false, EnumAction.NONE);
|
||||
this.ignoreLivingEntities(true);
|
||||
this.particleSpacing(0.5);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
// Needs to be outside because it gets run on the client-side
|
||||
if(caster instanceof EntityPlayer){
|
||||
if(caster.getHeldItemMainhand().getItem() instanceof ISpellCastingItem){
|
||||
caster.swingArm(EnumHand.MAIN_HAND);
|
||||
}else if(caster.getHeldItemOffhand().getItem() instanceof ISpellCastingItem){
|
||||
caster.swingArm(EnumHand.OFF_HAND);
|
||||
}
|
||||
}
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
if(WizardryUtilities.isBlockUnbreakable(world, pos)) return false;
|
||||
// The mine spell ignores the block damage setting for players, since that's the entire point of the spell
|
||||
// Instead, it triggers block break events at the appropriate points, which protection mods should be able to
|
||||
// pick up and allow/disallow accordingly
|
||||
// For the time being, dispensers respect the mobGriefing gamerule
|
||||
if(!(caster instanceof EntityPlayer) && !WizardryUtilities.canDamageBlocks(caster, world)) return false;
|
||||
// Can't mine arcane-locked blocks
|
||||
if(world.getTileEntity(pos) != null && world.getTileEntity(pos).getTileData().hasUniqueId(ArcaneLock.NBT_KEY)) return false;
|
||||
|
||||
IBlockState state = world.getBlockState(pos);
|
||||
// The maximum harvest level as determined by the potency multiplier. The + 0.5f is so that
|
||||
// weird float processing doesn't incorrectly round it down.
|
||||
int harvestLevel = (int)((modifiers.get(SpellModifiers.POTENCY) - 1) / Constants.POTENCY_INCREASE_PER_TIER + 0.5f);
|
||||
|
||||
if(harvestLevel > 0) harvestLevel--; // Shifts them all down one since normally novice wands give some potency
|
||||
|
||||
// The >= 3 is to allow master earth wands to break anything.
|
||||
if(state.getBlock().getHarvestLevel(state) <= harvestLevel || harvestLevel >= 3){
|
||||
|
||||
boolean flag = false;
|
||||
|
||||
int blastUpgradeCount = (int)((modifiers.get(WizardryItems.blast_upgrade) - 1) / Constants.RANGE_INCREASE_PER_LEVEL + 0.5f);
|
||||
// Results in the following patterns:
|
||||
// 0 blast upgrades: single block
|
||||
// 1 blast upgrade: 3x3 without corners or edges
|
||||
// 2 blast upgrades: 3x3 with corners
|
||||
// 3 blast upgrades: 5x5 without corners or edges
|
||||
float radius = 0.5f + 0.73f * blastUpgradeCount;
|
||||
|
||||
List<BlockPos> sphere = WizardryUtilities.getBlockSphere(pos, radius);
|
||||
|
||||
for(BlockPos pos1 : sphere){
|
||||
|
||||
if(WizardryUtilities.isBlockUnbreakable(world, pos1)) continue;
|
||||
|
||||
IBlockState state1 = world.getBlockState(pos1);
|
||||
|
||||
if(state1.getBlock().getHarvestLevel(state1) <= harvestLevel || harvestLevel >= 3){
|
||||
|
||||
if(caster instanceof EntityPlayerMP){ // Everything in here is server-side only so this is fine
|
||||
|
||||
boolean silkTouch = state1.getBlock().canSilkHarvest(world, pos1, state1, (EntityPlayer)caster)
|
||||
&& ItemArtefact.isArtefactActive((EntityPlayer)caster, WizardryItems.charm_silk_touch);
|
||||
|
||||
// Some protection mods seem to use this event instead so let's trigger it to check
|
||||
if(ForgeEventFactory.getBreakSpeed((EntityPlayer)caster, state1, 1, pos1) <= 0) continue;
|
||||
|
||||
int xp = ForgeHooks.onBlockBreakEvent(world,
|
||||
((EntityPlayerMP)caster).interactionManager.getGameType(), (EntityPlayerMP)caster, pos1);
|
||||
|
||||
if(xp == -1) continue; // Event was cancelled
|
||||
|
||||
if(silkTouch){
|
||||
flag = world.destroyBlock(pos1, false);
|
||||
if(flag) Block.spawnAsEntity(world, pos1, getSilkTouchDrop(state1));
|
||||
}else{
|
||||
flag = world.destroyBlock(pos1, true);
|
||||
if(flag) state1.getBlock().dropXpOnBlockBreak(world, pos1, xp);
|
||||
}
|
||||
|
||||
}else{
|
||||
// NPCs can dig the block under the target's feet
|
||||
flag = world.destroyBlock(pos1, true) || flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void spawnParticle(World world, double x, double y, double z, double vx, double vy, double vz){
|
||||
ParticleBuilder.create(Type.DUST).pos(x, y, z).time(20 + world.rand.nextInt(5)).clr(0.9f, 0.95f, 1)
|
||||
.shaded(false).spawn(world);
|
||||
}
|
||||
|
||||
// Copied from Block, where (for some reason) it's protected
|
||||
private static ItemStack getSilkTouchDrop(IBlockState state){
|
||||
|
||||
Item item = Item.getItemFromBlock(state.getBlock());
|
||||
int i = 0;
|
||||
|
||||
if(item.getHasSubtypes()){
|
||||
i = state.getBlock().getMetaFromState(state);
|
||||
}
|
||||
|
||||
return new ItemStack(item, 1, i);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
@@ -17,11 +14,11 @@ import net.minecraft.world.World;
|
||||
public class None extends Spell {
|
||||
|
||||
public None(){
|
||||
super("none", Tier.BASIC, Element.MAGIC, SpellType.UTILITY, 0, 0, EnumAction.NONE, false);
|
||||
super("none", EnumAction.NONE, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesSpellRequirePacket(){
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
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.LivingHurtEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class Paralysis extends SpellRay {
|
||||
|
||||
/** Creatures with this much health or less will snap out of paralysis - but only when they take damage, so a
|
||||
* creature on critical health may still be paralysed, but if it takes any damage at all the paralysis effect
|
||||
* will end. */
|
||||
private static final String CRITICAL_HEALTH = "critical_health";
|
||||
|
||||
public Paralysis(){
|
||||
super("paralysis", false, EnumAction.NONE);
|
||||
addProperties(DAMAGE, EFFECT_DURATION, CRITICAL_HEALTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
if(world.isRemote){
|
||||
// Rather neatly, the entity can be set here and if it's null nothing will happen.
|
||||
ParticleBuilder.create(Type.BEAM).entity(caster).clr(0.2f, 0.6f, 1)
|
||||
.pos(caster != null ? origin.subtract(caster.getPositionVector()) : origin).target(target).spawn(world);
|
||||
ParticleBuilder.create(Type.LIGHTNING).entity(caster)
|
||||
.pos(caster != null ? origin.subtract(caster.getPositionVector()) : origin).target(target).spawn(world);
|
||||
}
|
||||
|
||||
// This is a lot neater than it was, thanks to the damage type system.
|
||||
if(MagicDamage.isEntityImmune(DamageType.SHOCK, target)){
|
||||
if(!world.isRemote && caster instanceof EntityPlayer) ((EntityPlayer)caster).sendStatusMessage(
|
||||
new TextComponentTranslation("spell.resist",
|
||||
target.getName(), this.getNameForTranslationFormatted()), true);
|
||||
}else{
|
||||
target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.SHOCK),
|
||||
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
}
|
||||
|
||||
((EntityLivingBase)target).addPotionEffect(new PotionEffect(WizardryPotions.paralysis,
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)), 0));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
if(world.getBlockState(pos).getMaterial().isSolid()){
|
||||
Vec3d vec = hit.add(new Vec3d(side.getDirectionVec()).scale(WizardryUtilities.ANTI_Z_FIGHTING_OFFSET));
|
||||
ParticleBuilder.create(Type.SCORCH).pos(vec).face(side).clr(0.4f, 0.8f, 1).spawn(world);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
// This is first because we want the endpoint to be unaffected by the offset
|
||||
Vec3d endpoint = origin.add(direction.scale(getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade)));
|
||||
|
||||
if(world.isRemote){
|
||||
ParticleBuilder.create(Type.LIGHTNING).time(4).pos(origin).target(endpoint).scale(0.5f).spawn(world);
|
||||
ParticleBuilder.create(Type.BEAM).clr(0.2f, 0.6f, 1).time(4).pos(origin)
|
||||
.target(endpoint).spawn(world);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// See WizardryClientEventHandler for prevention of players' movement under the effects of paralysis
|
||||
|
||||
// TODO: (Animated?) screen overlay effect for paralysed players in first-person
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
// Disables entities' AI when under the effects of paralysis and re-enables it on the last update of the effect
|
||||
// - this can't be in the potion class because it requires access to the duration and hence the actual
|
||||
// PotionEffect instance
|
||||
if(event.getEntity() instanceof EntityLiving && event.getEntityLiving().isPotionActive(WizardryPotions.paralysis)){
|
||||
int timeLeft = event.getEntityLiving().getActivePotionEffect(WizardryPotions.paralysis).getDuration();
|
||||
((EntityLiving)event.getEntity()).setNoAI(timeLeft > 1);
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingHurtEvent(LivingHurtEvent event){
|
||||
// Paralysed creatures snap out of paralysis when they take critical damage
|
||||
if(event.getEntityLiving().isPotionActive(WizardryPotions.paralysis) && event.getEntityLiving().getHealth()
|
||||
- event.getAmount() <= Spells.paralysis.getProperty(CRITICAL_HEALTH).floatValue()){
|
||||
event.getEntityLiving().removePotionEffect(WizardryPotions.paralysis);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,6 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.block.BlockStatue;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -12,42 +9,43 @@ import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Petrify extends SpellRay {
|
||||
|
||||
private static final int BASE_DURATION = 900;
|
||||
// This is more descriptive and more accurate than the standard "effect_duration" in this case
|
||||
public static final String MINIMUM_EFFECT_DURATION = "minimum_effect_duration";
|
||||
|
||||
public Petrify(){
|
||||
super("petrify", Tier.ADVANCED, Element.SORCERY, SpellType.ATTACK, 40, 100, false, 10, SoundEvents.ENTITY_WITHER_SPAWN);
|
||||
super("petrify", false, EnumAction.NONE);
|
||||
this.soundValues(1, 1.1f, 0.2f);
|
||||
addProperties(MINIMUM_EFFECT_DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(target instanceof EntityLiving && !world.isRemote){
|
||||
// Unchecked cast is fine because the block is a static final field
|
||||
if(((BlockStatue)WizardryBlocks.petrified_stone).convertToStatue((EntityLiving)target,
|
||||
(int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)))){
|
||||
|
||||
return true;
|
||||
(int)(getProperty(MINIMUM_EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)))){
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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 onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,73 +2,39 @@ package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.RayTracer;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class PhaseStep extends Spell {
|
||||
|
||||
public static final String WALL_THICKNESS = "wall_thickness";
|
||||
|
||||
public PhaseStep(){
|
||||
super("phase_step", Tier.ADVANCED, Element.SORCERY, SpellType.UTILITY, 35, 40, EnumAction.NONE, false);
|
||||
super("phase_step", EnumAction.NONE, false);
|
||||
addProperties(RANGE, WALL_THICKNESS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
// Phase step does not gain range from range multiplier, instead it increases the thickness
|
||||
// of the wall you can teleport through.
|
||||
RayTraceResult rayTrace = WizardryUtilities.standardBlockRayTrace(world, caster, 5, false);
|
||||
|
||||
if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){
|
||||
|
||||
BlockPos pos = new BlockPos(rayTrace.getBlockPos().getX(), (int)caster.posY, rayTrace.getBlockPos().getZ());
|
||||
|
||||
// The maximum wall thickness as determined by the range multiplier. The + 0.5f is so that
|
||||
// weird float processing doesn't incorrectly round it down.
|
||||
int maxThickness = 1 + (int)((modifiers.get(WizardryItems.range_upgrade) - 1) / Constants.RANGE_INCREASE_PER_LEVEL + 0.5f);
|
||||
|
||||
if(rayTrace.sideHit.getAxis().isHorizontal()){
|
||||
|
||||
// i represents how far the player needs to teleport to get through the wall
|
||||
for(int i = 0; i <= maxThickness; i++){
|
||||
|
||||
BlockPos pos1 = pos.offset(rayTrace.sideHit.getOpposite(), i);
|
||||
|
||||
// Prevents the player from teleporting through unbreakable blocks, so they cannot cheat in other
|
||||
// mods' mazes and dungeons.
|
||||
if((WizardryUtilities.isBlockUnbreakable(world, pos1) || WizardryUtilities.isBlockUnbreakable(world, pos1.up()))
|
||||
&& !Wizardry.settings.teleportThroughUnbreakableBlocks)
|
||||
return false;
|
||||
|
||||
if(!world.getBlockState(pos1).getMaterial().blocksMovement()
|
||||
&& !world.getBlockState(pos1.up()).getMaterial().blocksMovement()){
|
||||
|
||||
if(!world.isRemote){
|
||||
caster.setPositionAndUpdate(pos1.getX() + 0.5, caster.posY, pos1.getZ() + 0.5);
|
||||
}
|
||||
|
||||
caster.swingArm(hand);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_ENDERMEN_TELEPORT, 1.0F, 1.0f);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
double range = getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade);
|
||||
RayTraceResult rayTrace = RayTracer.standardBlockRayTrace(world, caster, range, false);
|
||||
|
||||
// This is here because the conditions are false on the client for whatever reason. (see the Javadoc for cast()
|
||||
// for an explanation)
|
||||
if(world.isRemote){
|
||||
|
||||
for(int i = 0; i < 10; i++){
|
||||
double dx1 = caster.posX;
|
||||
double dy1 = caster.getEntityBoundingBox().minY + 2 * world.rand.nextFloat();
|
||||
@@ -76,12 +42,94 @@ public class PhaseStep extends Spell {
|
||||
world.spawnParticle(EnumParticleTypes.PORTAL, dx1, dy1, dz1, world.rand.nextDouble() - 0.5,
|
||||
world.rand.nextDouble() - 0.5, world.rand.nextDouble() - 0.5);
|
||||
}
|
||||
|
||||
|
||||
// Can't be bothered to route this through the proxies!
|
||||
electroblob.wizardry.client.WizardryClientEventHandler.playBlinkEffect();
|
||||
}
|
||||
|
||||
return false;
|
||||
if(rayTrace != null && rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){
|
||||
|
||||
BlockPos pos = rayTrace.getBlockPos();
|
||||
|
||||
// The maximum wall thickness as determined by the range multiplier. The + 0.5f is so that
|
||||
// weird float processing doesn't incorrectly round it down.
|
||||
int maxThickness = getProperty(WALL_THICKNESS).intValue()
|
||||
+ (int)((modifiers.get(WizardryItems.range_upgrade) - 1) / Constants.RANGE_INCREASE_PER_LEVEL + 0.5f);
|
||||
|
||||
if(rayTrace.sideHit == EnumFacing.UP) maxThickness++; // Allow space for the player's head
|
||||
|
||||
// i represents how far the player needs to teleport to get through the wall
|
||||
for(int i = 0; i <= maxThickness; i++){
|
||||
|
||||
BlockPos pos1 = pos.offset(rayTrace.sideHit.getOpposite(), i);
|
||||
|
||||
// Prevents the player from teleporting through unbreakable blocks, so they cannot cheat in other
|
||||
// mods' mazes and dungeons.
|
||||
if((WizardryUtilities.isBlockUnbreakable(world, pos1) || WizardryUtilities.isBlockUnbreakable(world, pos1.up()))
|
||||
&& !Wizardry.settings.teleportThroughUnbreakableBlocks)
|
||||
break; // Don't return false yet, there are other possible outcomes below now
|
||||
|
||||
if(!world.getBlockState(pos1).getMaterial().blocksMovement()
|
||||
&& !world.getBlockState(pos1.up()).getMaterial().blocksMovement()){
|
||||
|
||||
// Plays before and after so it is heard from both positions
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
if(!world.isRemote){
|
||||
caster.setPositionAndUpdate(pos1.getX() + 0.5, pos1.getY() + 0.5, pos1.getZ() + 0.5);
|
||||
}
|
||||
|
||||
caster.swingArm(hand);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If no suitable position was found on the other side of the wall, works like blink instead
|
||||
|
||||
// Leave space for the player's head
|
||||
if(rayTrace.sideHit == EnumFacing.DOWN) pos = pos.down();
|
||||
|
||||
// This means stuff like snow layers is ignored, meaning when on snow-covered ground the player does
|
||||
// not teleport 1 block above the ground.
|
||||
if(rayTrace.sideHit == EnumFacing.UP && !world.getBlockState(pos).getMaterial().blocksMovement()){
|
||||
pos = pos.down();
|
||||
}
|
||||
|
||||
pos = pos.offset(rayTrace.sideHit);
|
||||
|
||||
// Prevents the player from teleporting into blocks and suffocating
|
||||
if(world.getBlockState(pos).getMaterial().blocksMovement()
|
||||
|| world.getBlockState(pos.up()).getMaterial().blocksMovement()){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Plays before and after so it is heard from both positions
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
if(!world.isRemote) caster.setPositionAndUpdate(pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5);
|
||||
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
caster.swingArm(hand);
|
||||
return true;
|
||||
|
||||
}else{ // The ray trace missed
|
||||
|
||||
Vec3d destination = caster.getPositionVector().add(caster.getLookVec().scale(range));
|
||||
BlockPos pos = new BlockPos(destination);
|
||||
|
||||
// Prevents the player from teleporting into blocks and suffocating.
|
||||
if(world.getBlockState(pos).getMaterial().blocksMovement()
|
||||
|| world.getBlockState(pos.up()).getMaterial().blocksMovement()){
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!world.isRemote) caster.setPositionAndUpdate(destination.x, destination.y, destination.z);
|
||||
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
caster.swingArm(hand);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,54 +1,48 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.*;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class PlagueOfDarkness extends Spell {
|
||||
|
||||
private static final double BASE_RADIUS = 5;
|
||||
private static final float BASE_DAMAGE = 8;
|
||||
private static final int BASE_DURATION = 140;
|
||||
|
||||
public PlagueOfDarkness(){
|
||||
super("plague_of_darkness", Tier.MASTER, Element.NECROMANCY, SpellType.ATTACK, 75, 200, EnumAction.BOW, false);
|
||||
super("plague_of_darkness", EnumAction.BOW, false);
|
||||
addProperties(EFFECT_RADIUS, DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH);
|
||||
soundValues(1, 1.1f, 0.2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(
|
||||
BASE_RADIUS * modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world);
|
||||
double radius = getProperty(EFFECT_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade);
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(radius, caster.posX, caster.posY, caster.posZ, world);
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
if(WizardryUtilities.isValidTarget(caster, target)
|
||||
if(AllyDesignationSystem.isValidTarget(caster, target)
|
||||
&& !MagicDamage.isEntityImmune(DamageType.WITHER, target)){
|
||||
target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.WITHER),
|
||||
BASE_DAMAGE * modifiers.get(SpellModifiers.POTENCY));
|
||||
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
target.addPotionEffect(new PotionEffect(MobEffects.WITHER,
|
||||
(int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)), 2));
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)),
|
||||
getProperty(EFFECT_STRENGTH).intValue() + SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY))));
|
||||
}
|
||||
}
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
double particleX, particleZ;
|
||||
@@ -74,10 +68,16 @@ public class PlagueOfDarkness extends Spell {
|
||||
particleZ, particleX - caster.posX, 0, particleZ - caster.posZ, Block.getStateId(block));
|
||||
}
|
||||
}
|
||||
|
||||
ParticleBuilder.create(Type.SPHERE)
|
||||
.pos(caster.posX, caster.getEntityBoundingBox().minY + 0.1, caster.posZ)
|
||||
.scale((float)radius * 0.8f)
|
||||
.clr(0.8f, 0, 0.05f)
|
||||
.spawn(world);
|
||||
}
|
||||
|
||||
caster.swingArm(hand);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_DEATH, 1, 1 + 0.2f * world.rand.nextFloat());
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,29 +1,31 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.item.crafting.FurnaceRecipes;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class PocketFurnace extends Spell {
|
||||
|
||||
public static final String ITEMS_SMELTED = "items_smelted";
|
||||
|
||||
public PocketFurnace(){
|
||||
super("pocket_furnace", Tier.APPRENTICE, Element.FIRE, SpellType.UTILITY, 30, 40, EnumAction.BOW, false);
|
||||
super("pocket_furnace", EnumAction.BOW, false);
|
||||
addProperties(ITEMS_SMELTED);
|
||||
soundValues(1, 0.75f, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
int usesLeft = 5;
|
||||
int usesLeft = (int)(getProperty(ITEMS_SMELTED).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
ItemStack stack, result;
|
||||
|
||||
@@ -31,11 +33,14 @@ public class PocketFurnace extends Spell {
|
||||
|
||||
stack = caster.inventory.getStackInSlot(i);
|
||||
|
||||
if(!stack.isEmpty()){
|
||||
if(!stack.isEmpty() && !world.isRemote){
|
||||
|
||||
result = FurnaceRecipes.instance().getSmeltingResult(stack);
|
||||
|
||||
if(!result.isEmpty()){
|
||||
if(!result.isEmpty() && !(result.getItem() instanceof ItemTool) && !(result.getItem() instanceof ItemSword)
|
||||
&& !(result.getItem() instanceof ItemArmor)
|
||||
&& !Arrays.asList(Wizardry.settings.pocketFurnaceItemBlacklist).contains(result.getItem().getRegistryName())){
|
||||
|
||||
if(stack.getCount() <= usesLeft){
|
||||
ItemStack stack2 = new ItemStack(result.getItem(), stack.getCount(), result.getItemDamage());
|
||||
if(WizardryUtilities.doesPlayerHaveItem(caster, result.getItem())){
|
||||
@@ -55,7 +60,7 @@ public class PocketFurnace extends Spell {
|
||||
}
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_FURNACE_FIRE_CRACKLE, 1, 0.75f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
if(world.isRemote){
|
||||
for(int i = 0; i < 10; i++){
|
||||
|
||||
@@ -2,12 +2,7 @@ package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.WizardryGuiHandler;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
@@ -16,11 +11,11 @@ import net.minecraft.world.World;
|
||||
public class PocketWorkbench extends Spell {
|
||||
|
||||
public PocketWorkbench(){
|
||||
super("pocket_workbench", Tier.APPRENTICE, Element.SORCERY, SpellType.UTILITY, 30, 40, EnumAction.BOW, false);
|
||||
super("pocket_workbench", EnumAction.BOW, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesSpellRequirePacket(){
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -33,7 +28,7 @@ public class PocketWorkbench extends Spell {
|
||||
(int)caster.posY, (int)caster.posZ);
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1, 1);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -13,20 +9,22 @@ import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
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;
|
||||
|
||||
public class Poison extends SpellRay {
|
||||
|
||||
private static final int BASE_DURATION = 200;
|
||||
|
||||
public Poison(){
|
||||
super("poison", Tier.APPRENTICE, Element.EARTH, SpellType.ATTACK, 10, 20, false, 10, WizardrySounds.SPELL_ICE);
|
||||
super("poison", false, EnumAction.NONE);
|
||||
this.soundValues(1, 1.1f, 0.2f);
|
||||
addProperties(DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,7 +33,7 @@ public class Poison extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
@@ -45,24 +43,23 @@ public class Poison extends SpellRay {
|
||||
new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted()), true);
|
||||
}else{
|
||||
target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.POISON),
|
||||
1 * modifiers.get(SpellModifiers.POTENCY));
|
||||
getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
((EntityLivingBase)target).addPotionEffect(new PotionEffect(MobEffects.POISON,
|
||||
(int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)), 1));
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)),
|
||||
getProperty(EFFECT_STRENGTH).intValue() + SpellBuff.getStandardBonusAmplifier(modifiers.get(SpellModifiers.POTENCY))));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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 onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,714 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Multimap;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.data.IVariable;
|
||||
import electroblob.wizardry.data.IVariable.Variable;
|
||||
import electroblob.wizardry.data.Persistence;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.entity.living.*;
|
||||
import electroblob.wizardry.entity.projectile.*;
|
||||
import electroblob.wizardry.integration.DamageSafetyChecker;
|
||||
import electroblob.wizardry.packet.PacketControlInput;
|
||||
import electroblob.wizardry.packet.PacketPossession;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.*;
|
||||
import net.minecraft.entity.ai.EntityAIAttackMelee;
|
||||
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.ai.attributes.IAttribute;
|
||||
import net.minecraft.entity.ai.attributes.IAttributeInstance;
|
||||
import net.minecraft.entity.monster.*;
|
||||
import net.minecraft.entity.passive.EntityChicken;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.entity.projectile.EntityPotion;
|
||||
import net.minecraft.entity.projectile.EntitySnowball;
|
||||
import net.minecraft.init.Enchantments;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.init.PotionTypes;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.ItemBow;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagList;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.potion.PotionUtils;
|
||||
import net.minecraft.util.*;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
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.common.util.Constants.NBT;
|
||||
import net.minecraftforge.event.entity.item.ItemTossEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDamageEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDeathEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
|
||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
||||
import net.minecraftforge.event.entity.player.EntityItemPickupEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventPriority;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.PlayerEvent;
|
||||
import net.minecraftforge.fml.common.gameevent.TickEvent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class Possession extends SpellRay {
|
||||
|
||||
/** A {@code ResourceLocation} representing the shader file used when possessing an entity. */
|
||||
public static final ResourceLocation SHADER = new ResourceLocation(Wizardry.MODID, "shaders/post/possession.json");
|
||||
|
||||
/** The NBT tag name for storing the possessing entity's UUID in the target's tag compound. */
|
||||
public static final String NBT_KEY = "possessor";
|
||||
/** The NBT tag name for storing the possessor's previous inventory in their tag compound. */
|
||||
public static final String INVENTORY_NBT_KEY = "prevInventory";
|
||||
|
||||
/** The health (in half-hearts) below or equal to which the possessor will automatically stop possessing. */
|
||||
public static final String CRITICAL_HEALTH = "critical_health";
|
||||
|
||||
private static final int PROJECTILE_COOLDOWN = 30;
|
||||
|
||||
public static final IVariable<Integer> TIMER_KEY = new Variable<Integer>(Persistence.DIMENSION_CHANGE).withTicker(Possession::update);
|
||||
public static final IVariable<EntityLiving> POSSESSEE_KEY = new Variable<>(Persistence.DIMENSION_CHANGE);
|
||||
public static final IVariable<Integer> SHOOT_COOLDOWN_KEY = new Variable<Integer>(Persistence.DIMENSION_CHANGE).withTicker((p, n) -> Math.max(n-1, 0));
|
||||
|
||||
private static final Multimap<Class<? extends EntityLiving>, BiConsumer<?, EntityPlayer>> abilities = HashMultimap.create();
|
||||
private static final Map<Class<? extends EntityLiving>, Function<World, ? extends IProjectile>> projectiles = new HashMap<>();
|
||||
|
||||
private static final Map<IAttribute, UUID> INHERITED_ATTRIBUTES;
|
||||
|
||||
static {
|
||||
|
||||
INHERITED_ATTRIBUTES = ImmutableMap.of(
|
||||
SharedMonsterAttributes.MOVEMENT_SPEED, UUID.fromString("f65cfcaf-e7ec-4dfb-aa6c-711735d007e3"),
|
||||
SharedMonsterAttributes.ATTACK_DAMAGE, UUID.fromString("ab67c89e-74a5-4e27-9621-40bffb4f7a03"),
|
||||
SharedMonsterAttributes.KNOCKBACK_RESISTANCE, UUID.fromString("05529535-9bcf-42bb-8822-45f5ce6a8f08"));
|
||||
|
||||
addAbility(EntitySpider.class, (spider, player) -> { if(player.collidedHorizontally) player.motionY = 0.2; });
|
||||
addAbility(EntityChicken.class, (chicken, player) -> { if(!player.onGround && player.motionY < 0) player.motionY *= 0.6D; });
|
||||
addAbility(EntityLiving.class, (entity, player) -> { if(!entity.isImmuneToFire() && player.isBurning()) player.extinguish(); });
|
||||
|
||||
addProjectile(EntitySnowman.class, EntitySnowball::new); // Woooo snowballs!
|
||||
addProjectile(EntityBlaze.class, EntityMagicFireball::new); // Ugh normal fireballs don't fit so let's just use mine!
|
||||
addProjectile(EntityGhast.class, EntityLargeMagicFireball::new);
|
||||
addProjectile(EntityIceWraith.class, EntityIceShard::new);
|
||||
addProjectile(EntityShadowWraith.class, EntityDarknessOrb::new);
|
||||
addProjectile(EntityStormElemental.class, EntityLightningDisc::new);
|
||||
addProjectile(EntityWitch.class, EntityPotion::new);
|
||||
|
||||
}
|
||||
|
||||
public Possession(){
|
||||
super("possession", false, EnumAction.NONE);
|
||||
addProperties(EFFECT_DURATION, CRITICAL_HEALTH);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs() { return false; }
|
||||
@Override public boolean canBeCastByDispensers() { return false; }
|
||||
|
||||
@Override
|
||||
public boolean requiresPacket(){
|
||||
return false; // Has its own packet
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent[] createSounds(){
|
||||
return createSoundsWithSuffixes("possess", "end");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
Vec3d look = caster.getLookVec();
|
||||
Vec3d origin = new Vec3d(caster.posX, caster.getEntityBoundingBox().minY + caster.getEyeHeight() - Y_OFFSET, caster.posZ);
|
||||
|
||||
if(!shootSpell(world, origin, look, caster, ticksInUse, modifiers)) return false;
|
||||
|
||||
if(casterSwingsArm(world, caster, hand, ticksInUse, modifiers)) caster.swingArm(hand);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers, "possess"); // TODO: There must be a better way...
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse,
|
||||
SpellModifiers modifiers){
|
||||
|
||||
if(target instanceof EntityLiving && caster instanceof EntityPlayer && !isPossessing((EntityPlayer)caster)){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)caster;
|
||||
|
||||
if(!player.isCreative() && player.getHealth() <= getProperty(CRITICAL_HEALTH).floatValue()){
|
||||
player.sendStatusMessage(new TextComponentTranslation(
|
||||
"spell." + this.getRegistryName() + ".insufficienthealth"), true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!world.isRemote){
|
||||
int duration = (int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade));
|
||||
if(possess(player, (EntityLiving)target, duration)){
|
||||
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;
|
||||
}
|
||||
|
||||
// ================================================ Helper methods ================================================
|
||||
|
||||
/**
|
||||
* Causes the given player to start possessing the given target for the given duration, and sets all relevant data
|
||||
* for both entities accordingly. Also takes care of sending packets to update clients.
|
||||
* @param possessor The player doing the possessing.
|
||||
* @param target The entity being possessed.
|
||||
* @param duration The number of ticks for which the possession should last. Pass in a negative integer to make the
|
||||
* possession last indefinitely (until manually ended with the dismount key).
|
||||
* @return True if the possession succeeded, false if for some reason it did not (only happens if the player's
|
||||
* {@link WizardData} is null).
|
||||
*/
|
||||
public boolean possess(EntityPlayer possessor, EntityLiving target, int duration){
|
||||
|
||||
if(WizardData.get(possessor) != null){
|
||||
|
||||
WizardData.get(possessor).setVariable(POSSESSEE_KEY, target);
|
||||
WizardData.get(possessor).setVariable(TIMER_KEY, duration);
|
||||
|
||||
possessor.setPositionAndRotation(target.posX, target.posY, target.posZ, target.rotationYaw, target.rotationPitch);
|
||||
possessor.eyeHeight = target.getEyeHeight();
|
||||
setSize(possessor, target.width, target.height);
|
||||
|
||||
target.setDead();
|
||||
target.setNoAI(true);
|
||||
target.setAttackTarget(null);
|
||||
|
||||
// Attributes
|
||||
|
||||
if(target instanceof EntityFlying || target instanceof net.minecraft.entity.passive.EntityFlying){
|
||||
possessor.capabilities.allowFlying = true;
|
||||
possessor.capabilities.isFlying = true;
|
||||
}
|
||||
|
||||
// Apply attribute modifiers which change the player's attribute value to the target's value
|
||||
// Uses predefined UUIDs so we can easily remove them later
|
||||
attributes:
|
||||
for(IAttribute attribute : INHERITED_ATTRIBUTES.keySet()){
|
||||
|
||||
IAttributeInstance instance = target.getAttributeMap().getAttributeInstance(attribute);
|
||||
|
||||
if(instance != null){
|
||||
|
||||
double targetValue = instance.getAttributeValue();
|
||||
double currentValue = possessor.getAttributeMap().getAttributeInstance(attribute).getAttributeValue();
|
||||
// Don't ask me why, but the player's base movement speed seems to be 0.1
|
||||
if(attribute == SharedMonsterAttributes.MOVEMENT_SPEED) currentValue /= possessor.capabilities.getWalkSpeed();
|
||||
|
||||
for(EntityEquipmentSlot slot : EntityEquipmentSlot.values()){
|
||||
if(target.getItemStackFromSlot(slot).getAttributeModifiers(slot).containsKey(attribute.getName())){
|
||||
// If the mob has equipment, use the modifiers for that equipment instead of the mob's normal ones
|
||||
// Not doing this results in the player being able to one-hit most mobs when possessing a zombie pigman!
|
||||
continue attributes;
|
||||
}
|
||||
}
|
||||
|
||||
possessor.getAttributeMap().getAttributeInstance(attribute).applyModifier(new AttributeModifier(
|
||||
INHERITED_ATTRIBUTES.get(attribute), "possessionModifier", targetValue / currentValue,
|
||||
WizardryUtilities.Operations.MULTIPLY_FLAT));
|
||||
}
|
||||
}
|
||||
|
||||
if(possessor.world.isRemote){
|
||||
// Shaders and effects
|
||||
if(Wizardry.settings.useShaders) net.minecraft.client.Minecraft.getMinecraft().entityRenderer.loadShader(SHADER);
|
||||
electroblob.wizardry.client.WizardryClientEventHandler.playBlinkEffect(); // Looks quite nice...
|
||||
|
||||
}else{
|
||||
|
||||
// Targeting
|
||||
|
||||
for(EntityLiving creature : WizardryUtilities.getEntitiesWithinRadius(16, possessor.posX,
|
||||
possessor.posY, possessor.posZ, possessor.world, EntityLiving.class)){
|
||||
// Mobs are dumb, if a player possesses something they're like "Huh?! Where'd you go?"
|
||||
// Of course, this won't last long if the player attacks them, since they'll revenge-target them
|
||||
if(creature.getAttackTarget() == possessor && !creature.canAttackClass(target.getClass()))
|
||||
creature.setAttackTarget(null);
|
||||
}
|
||||
|
||||
// Inventory and items
|
||||
|
||||
if(possessor.getEntityData() != null){
|
||||
possessor.getEntityData().setTag(INVENTORY_NBT_KEY, possessor.inventory.writeToNBT(new NBTTagList()));
|
||||
}
|
||||
|
||||
possessor.inventory.clear();
|
||||
possessor.inventoryContainer.detectAndSendChanges();
|
||||
|
||||
ItemStack stack = target.getHeldItemMainhand().copy();
|
||||
|
||||
if(target instanceof EntityEnderman && ((EntityEnderman)target).getHeldBlockState() != null){
|
||||
stack = new ItemStack(((EntityEnderman)target).getHeldBlockState().getBlock());
|
||||
|
||||
}else if(stack.getItem() instanceof ItemBow){
|
||||
Map<Enchantment, Integer> enchantments = EnchantmentHelper.getEnchantments(stack);
|
||||
enchantments.put(Enchantments.INFINITY, 1);
|
||||
EnchantmentHelper.setEnchantments(enchantments, stack);
|
||||
ItemStack arrow = new ItemStack(Items.ARROW);
|
||||
if(target instanceof EntityStray || target instanceof EntityStrayMinion){
|
||||
arrow = new ItemStack(Items.TIPPED_ARROW);
|
||||
PotionUtils.addPotionToItemStack(arrow, PotionTypes.SLOWNESS);
|
||||
}
|
||||
possessor.setHeldItem(EnumHand.OFF_HAND, arrow);
|
||||
}
|
||||
|
||||
possessor.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, stack);
|
||||
|
||||
// Packets
|
||||
|
||||
WizardryPacketHandler.net.sendToAllTracking(new PacketPossession.Message(possessor, target, duration), possessor);
|
||||
if(possessor instanceof EntityPlayerMP){
|
||||
WizardryPacketHandler.net.sendTo(new PacketPossession.Message(possessor, target, duration), (EntityPlayerMP)possessor);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Causes the given player to stop possessing their current possessee, if any, and resets all relevant data for
|
||||
* both entities. Also takes care of sending packets to update clients. */
|
||||
public void endPossession(EntityPlayer player){
|
||||
|
||||
// Reverts the possessed entity back to normal
|
||||
|
||||
EntityLiving victim = getPossessee(player);
|
||||
|
||||
if(victim != null){
|
||||
|
||||
victim.isDead = false;
|
||||
victim.setNoAI(false);
|
||||
victim.setPosition(player.posX, player.posY, player.posZ);
|
||||
if(!player.world.isRemote) player.world.spawnEntity(victim);
|
||||
|
||||
for(PotionEffect effect : player.getActivePotionEffects()){
|
||||
victim.addPotionEffect(effect);
|
||||
}
|
||||
}
|
||||
|
||||
// Reverts the player back to normal
|
||||
|
||||
player.clearActivePotions();
|
||||
|
||||
player.eyeHeight = player.getDefaultEyeHeight(); // How convenient!
|
||||
|
||||
if(WizardData.get(player) != null){
|
||||
WizardData.get(player).setVariable(TIMER_KEY, 0);
|
||||
WizardData.get(player).setVariable(POSSESSEE_KEY, null);
|
||||
}
|
||||
|
||||
if(!player.capabilities.isCreativeMode){
|
||||
player.capabilities.allowFlying = false;
|
||||
player.capabilities.isFlying = false;
|
||||
}
|
||||
|
||||
if(player.world.isRemote){
|
||||
net.minecraft.client.Minecraft.getMinecraft().entityRenderer.stopUseShader();
|
||||
electroblob.wizardry.client.WizardryClientEventHandler.playBlinkEffect(); // Looks quite nice...
|
||||
}
|
||||
|
||||
for(IAttribute attribute : INHERITED_ATTRIBUTES.keySet()){
|
||||
player.getAttributeMap().getAttributeInstance(attribute).removeModifier(INHERITED_ATTRIBUTES.get(attribute));
|
||||
}
|
||||
|
||||
if(player instanceof EntityPlayerMP){
|
||||
|
||||
player.inventory.clear();
|
||||
|
||||
if(player.getEntityData() != null){
|
||||
player.inventory.readFromNBT(player.getEntityData().getTagList(INVENTORY_NBT_KEY, NBT.TAG_COMPOUND));
|
||||
}
|
||||
|
||||
player.inventoryContainer.detectAndSendChanges();
|
||||
}
|
||||
|
||||
this.playSound(player.world, player, 0, -1, null, "end");
|
||||
|
||||
if(!player.world.isRemote && player instanceof EntityPlayerMP){
|
||||
WizardryPacketHandler.net.sendToAllTracking(new PacketPossession.Message(player, null, 0), player);
|
||||
WizardryPacketHandler.net.sendTo(new PacketPossession.Message(player, null, 0), (EntityPlayerMP)player);
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns the {@code EntityLiving} that is currently being possessed by the given player, or null if the player is
|
||||
* not currently possessing an entity. */
|
||||
@Nullable
|
||||
public static EntityLiving getPossessee(EntityPlayer player){
|
||||
return WizardData.get(player) == null ? null : WizardData.get(player).getVariable(POSSESSEE_KEY);
|
||||
}
|
||||
|
||||
/** Returns true if the given player is currently possessing an entity, false otherwise. Just a shortcut for
|
||||
* {@code Possession.getPossessee(player) != null}. */
|
||||
public static boolean isPossessing(EntityPlayer player){
|
||||
return getPossessee(player) != null;
|
||||
}
|
||||
|
||||
private static int update(EntityPlayer player, Integer possessionTimer){
|
||||
|
||||
if(possessionTimer == null) possessionTimer = 0;
|
||||
|
||||
if(possessionTimer > 0){
|
||||
|
||||
if(isPossessing(player) && !player.isSneaking()){
|
||||
|
||||
possessionTimer--;
|
||||
|
||||
if(player.world.isRemote){
|
||||
ParticleBuilder.create(Type.DARK_MAGIC, player).clr(0.1f, 0, 0.3f).spawn(player.world);
|
||||
// TODO: This (and a few other similar uses) needs ClientProxy-ing.
|
||||
if(!net.minecraft.client.Minecraft.getMinecraft().entityRenderer.isShaderActive())
|
||||
if(Wizardry.settings.useShaders) net.minecraft.client.Minecraft.getMinecraft().entityRenderer.loadShader(SHADER);
|
||||
}
|
||||
|
||||
}else{
|
||||
((Possession)Spells.possession).endPossession(player);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}else if(isPossessing(player)){
|
||||
((Possession)Spells.possession).endPossession(player);
|
||||
}
|
||||
|
||||
return possessionTimer;
|
||||
}
|
||||
|
||||
/** Adds the given {@link BiConsumer} to the list of abilities. An <i>ability</i> is an entity-specific action or
|
||||
* effect that happens when a certain type of entity is possessed. For example, spiders can climb walls, endermen
|
||||
* can pick up blocks, creepers explode, etc. Other mods may use this method to */
|
||||
public static <T extends EntityLiving> void addAbility(Class<T> entityType, BiConsumer<T, EntityPlayer> ability){
|
||||
abilities.put(entityType, ability);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // Guess what? Type erasure again!
|
||||
private static <T extends EntityLiving> void performAbilities(EntityLiving entity, EntityPlayer player){
|
||||
// Now we have a type parameter T to work with we can ram the entity into the consumer without a compiler error
|
||||
for(Class<? extends EntityLiving> entityType : abilities.keySet()){
|
||||
if(entityType.isAssignableFrom(entity.getClass())){
|
||||
abilities.get(entityType).forEach(a -> ((BiConsumer<T, EntityPlayer>)a).accept((T)entity, player));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds the given factory to the list of projectiles. When a player right-clicks while possessing an entity of the
|
||||
* given type, the given projectile factory will be invoked to create a projectile, which is then aimed and spawned. */
|
||||
public static <T extends Entity & IProjectile> void addProjectile(Class<? extends EntityLiving> entityType, Function<World, T> factory){
|
||||
projectiles.put(entityType, factory);
|
||||
}
|
||||
|
||||
/** Copied from Entity#setSize, with the call to move(...) removed. This is presumably also better than reflecting
|
||||
* into Entity#setSize, which is protected. */
|
||||
private static void setSize(Entity entity, float width, float height){
|
||||
|
||||
if(width != entity.width || height != entity.height){
|
||||
|
||||
entity.width = width;
|
||||
entity.height = height;
|
||||
|
||||
double halfWidth = (double)width / 2.0D;
|
||||
entity.setEntityBoundingBox(new AxisAlignedBB(entity.posX - halfWidth, entity.posY, entity.posZ - halfWidth, entity.posX + halfWidth, entity.posY + (double)entity.height, entity.posZ + halfWidth));
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================ Event Handlers ================================================
|
||||
// We got every kind of event handler goin', folks!
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){
|
||||
|
||||
if(event.phase == TickEvent.Phase.START){
|
||||
|
||||
EntityLiving possessee = getPossessee(event.player);
|
||||
|
||||
if(possessee != null){
|
||||
// Updating these to the player's variables won't have an effect on player movement, but it will
|
||||
// affect various bits of mob-specific logic
|
||||
possessee.setPosition(event.player.posX, event.player.posY, event.player.posZ);
|
||||
possessee.motionX = event.player.motionX;
|
||||
possessee.motionY = event.player.motionY;
|
||||
possessee.motionZ = event.player.motionZ;
|
||||
possessee.onGround = event.player.onGround;
|
||||
|
||||
possessee.onUpdate(); // Event though it's not in the world, it still needs updating
|
||||
possessee.ticksExisted++; // Normally gets updated from World
|
||||
|
||||
if(possessee.getHealth() <= 0){
|
||||
((Possession)Spells.possession).endPossession(event.player);
|
||||
}
|
||||
|
||||
performAbilities(possessee, event.player);
|
||||
}
|
||||
}
|
||||
|
||||
// Right at the end of EntityPlayer#onUpdate() it calls EntityPlayer#updateSize(), which resets the player's
|
||||
// size (and is also where this event is fired from, oddly enough) ... but not on my watch!
|
||||
if(event.phase == TickEvent.Phase.END){
|
||||
EntityLiving possessee = getPossessee(event.player);
|
||||
if(possessee != null){
|
||||
setSize(event.player, possessee.width, possessee.height);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When possessing, attacks are diverted to the possessed entity for armour, immunity, resistance calculations
|
||||
// and so on, then when the damage is actually applied, the player is also damaged via onLivingDamageEvent below
|
||||
@SubscribeEvent(priority = EventPriority.HIGH)
|
||||
public static void onLivingAttackEvent(LivingAttackEvent event){
|
||||
|
||||
if(event.getEntity() instanceof EntityPlayer && event.getSource() != DamageSource.OUT_OF_WORLD){
|
||||
|
||||
EntityLiving possessee = getPossessee((EntityPlayer)event.getEntity());
|
||||
|
||||
if(possessee != null){
|
||||
DamageSafetyChecker.attackEntitySafely(possessee, event.getSource(), event.getAmount(), event.getSource().getDamageType());
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LivingDamageEvent used in preference to LivingHurtEvent because the player is 'inside' the possessed entity, so
|
||||
// any damage should come through that entity (and any armour, potions, enchantments etc. it has) first.
|
||||
@SubscribeEvent
|
||||
public static void onLivingDamageEvent(LivingDamageEvent event){
|
||||
|
||||
for(EntityPlayer player : event.getEntity().world.playerEntities){
|
||||
|
||||
EntityLiving possessee = getPossessee(player);
|
||||
|
||||
if(possessee == event.getEntity()){
|
||||
// Possessors take half of all damage taken by the entity they are possessing. If the possessor receives
|
||||
// fatal/critical damage (i.e. damage that takes them to half a heart or less), their health is reset to half
|
||||
// a heart and the possession ends.
|
||||
if(!player.capabilities.isCreativeMode){
|
||||
// TODO: Make this a proper DamageSource?
|
||||
DamageSafetyChecker.attackEntitySafely(player, DamageSource.OUT_OF_WORLD, event.getAmount() / 2,
|
||||
DamageSource.OUT_OF_WORLD.getDamageType());
|
||||
}
|
||||
|
||||
if(player.getHealth() <= Spells.possession.getProperty(CRITICAL_HEALTH).floatValue()){
|
||||
player.setHealth(Spells.possession.getProperty(CRITICAL_HEALTH).floatValue());
|
||||
((Possession)Spells.possession).endPossession(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Prevents possessing players from interacting with blocks and controls projectile shooting
|
||||
@SubscribeEvent
|
||||
public static void onPlayerInteractEvent(PlayerInteractEvent event){
|
||||
|
||||
if(event instanceof PlayerInteractEvent.RightClickItem) return; // Can always do this
|
||||
|
||||
EntityLiving possessee = getPossessee(event.getEntityPlayer());
|
||||
|
||||
if(possessee != null){
|
||||
|
||||
// Let endermen interact with blocks
|
||||
if(possessee instanceof EntityEnderman && (
|
||||
(event instanceof PlayerInteractEvent.RightClickBlock && ((EntityEnderman)possessee).getHeldBlockState() != null)
|
||||
|| (event instanceof PlayerInteractEvent.LeftClickBlock && ((EntityEnderman)possessee).getHeldBlockState() == null)))
|
||||
return;
|
||||
|
||||
if(WizardData.get(event.getEntityPlayer()) != null && event.getWorld().isRemote
|
||||
&& (event instanceof PlayerInteractEvent.RightClickEmpty
|
||||
|| event instanceof PlayerInteractEvent.EntityInteract
|
||||
|| event instanceof PlayerInteractEvent.RightClickBlock)){
|
||||
|
||||
Integer cooldown = WizardData.get(event.getEntityPlayer()).getVariable(SHOOT_COOLDOWN_KEY);
|
||||
|
||||
if(cooldown == null || cooldown == 0){
|
||||
|
||||
WizardryPacketHandler.net.sendToServer(new PacketControlInput.Message(PacketControlInput.ControlType.POSSESSION_PROJECTILE));
|
||||
WizardData.get(event.getEntityPlayer()).setVariable(SHOOT_COOLDOWN_KEY, PROJECTILE_COOLDOWN);
|
||||
|
||||
if(possessee instanceof EntityLightningWraith){
|
||||
Spells.arc.cast(event.getWorld(), event.getEntityPlayer(), EnumHand.MAIN_HAND, 0, new SpellModifiers());
|
||||
}
|
||||
|
||||
if(possessee instanceof EntityCreeper){
|
||||
((EntityCreeper)possessee).ignite();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(event.isCancelable()) event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
|
||||
/** Called via packets to shoot a projectile if the entity currently possessed by the given player can do so. */
|
||||
public static void shootProjectile(EntityPlayer possessor){
|
||||
|
||||
if(WizardData.get(possessor) != null){
|
||||
|
||||
Integer cooldown = WizardData.get(possessor).getVariable(SHOOT_COOLDOWN_KEY);
|
||||
|
||||
if(cooldown == null || cooldown == 0){
|
||||
|
||||
EntityLiving possessee = getPossessee(possessor);
|
||||
|
||||
if(possessee != null){
|
||||
|
||||
if(possessee instanceof EntityLightningWraith){
|
||||
Spells.arc.cast(possessor.world, possessor, EnumHand.MAIN_HAND, 0, new SpellModifiers());
|
||||
}
|
||||
|
||||
if(possessee instanceof EntityCreeper){
|
||||
((Possession)Spells.possession).endPossession(possessor);
|
||||
((EntityCreeper)possessee).ignite(); // Aaaaaaand.... RUN!
|
||||
}
|
||||
|
||||
Function<World, ? extends IProjectile> factory = projectiles.get(possessee.getClass());
|
||||
|
||||
if(factory != null){
|
||||
|
||||
IProjectile projectile = factory.apply(possessor.world);
|
||||
Vec3d look = possessor.getLookVec();
|
||||
((Entity)projectile).setPosition(possessor.posX + look.x, possessor.posY + possessor.getEyeHeight() + look.y, possessor.posZ + look.z);
|
||||
projectile.shoot(look.x, look.y, look.z, 1.6f, WizardryUtilities.getDefaultAimingError(possessor.world.getDifficulty()));
|
||||
|
||||
if(projectile instanceof EntityMagicProjectile) ((EntityMagicProjectile)projectile).setCaster(possessor);
|
||||
else if(projectile instanceof EntityMagicArrow) ((EntityMagicArrow)projectile).setCaster(possessor);
|
||||
|
||||
possessor.world.spawnEntity((Entity)projectile);
|
||||
|
||||
}
|
||||
|
||||
WizardData.get(possessor).setVariable(SHOOT_COOLDOWN_KEY, PROJECTILE_COOLDOWN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){ // Not fired for revenge-targeting
|
||||
if(event.getTarget() instanceof EntityPlayer && event.getEntityLiving() instanceof EntityLiving){
|
||||
EntityLiving possessee = getPossessee((EntityPlayer)event.getTarget());
|
||||
EntityLiving attacker = (EntityLiving)event.getEntityLiving();
|
||||
if(possessee != null && !attacker.canAttackClass(possessee.getClass())){
|
||||
event.setCanceled(true); // Mobs can't target a player possessing an entity they don't normally attack
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// With these two methods I'm pretty sure it's watertight
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingDeathEvent(LivingDeathEvent event){
|
||||
if(event.getEntity() instanceof EntityPlayer && isPossessing((EntityPlayer)event.getEntity())){
|
||||
((Possession)Spells.possession).endPossession((EntityPlayer)event.getEntity()); // Just in case, to make sure the player drops their items
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerLoggedOutEvent(PlayerEvent.PlayerLoggedOutEvent event){
|
||||
if(isPossessing(event.player)) ((Possession)Spells.possession).endPossession(event.player);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onBlockBreakEvent(BlockEvent.BreakEvent event){
|
||||
|
||||
EntityLiving possessee = getPossessee(event.getPlayer());
|
||||
|
||||
if(possessee instanceof EntityEnderman){
|
||||
if(((EntityEnderman)possessee).getHeldBlockState() == null){
|
||||
((EntityEnderman)possessee).setHeldBlockState(event.getState());
|
||||
event.getPlayer().setHeldItem(EnumHand.MAIN_HAND, new ItemStack(event.getState().getBlock()));
|
||||
event.setExpToDrop(0);
|
||||
event.getWorld().setBlockToAir(event.getPos()); // Remove block before it can drop
|
||||
}else{
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onBlockPlaceEvent(BlockEvent.PlaceEvent event){
|
||||
|
||||
EntityLiving possessee = getPossessee(event.getPlayer());
|
||||
|
||||
if(possessee instanceof EntityEnderman){
|
||||
if(((EntityEnderman)possessee).getHeldBlockState() == event.getState()){
|
||||
((EntityEnderman)possessee).setHeldBlockState(null);
|
||||
}else{
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onEntityItemPickupEvent(EntityItemPickupEvent event){ // Why are there two item pickup events?
|
||||
|
||||
EntityLiving possessee = getPossessee(event.getEntityPlayer());
|
||||
|
||||
if(possessee != null){
|
||||
|
||||
if(possessee.canPickUpLoot() && possessee.getHeldItemMainhand().isEmpty()){
|
||||
possessee.setHeldItem(EnumHand.MAIN_HAND, event.getItem().getItem());
|
||||
}else{
|
||||
event.setCanceled(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
static void onItemTossEvent(ItemTossEvent event){
|
||||
if(isPossessing(event.getPlayer())){ // Can't drop items while possessing
|
||||
event.setCanceled(true);
|
||||
event.getPlayer().inventory.addItemStackToInventory(event.getEntityItem().getItem());
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onAttackEntityEvent(AttackEntityEvent event){
|
||||
|
||||
EntityLiving possessee = getPossessee(event.getEntityPlayer());
|
||||
|
||||
if(possessee == null) return;
|
||||
|
||||
if(possessee instanceof EntityCreeper){
|
||||
event.setCanceled(true); // Why do creepers have a melee AI?!
|
||||
}else if(possessee.tasks.taskEntries.stream().noneMatch(t -> t.action instanceof EntityAIAttackMelee)){
|
||||
event.setCanceled(true); // Can't melee with a non-melee mob
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class RayOfPurification extends SpellRay {
|
||||
|
||||
/** The number by which this spell's damage is multiplied for undead entities. */
|
||||
public static final String UNDEAD_DAMAGE_MULTIPLIER = "undead_damage_multiplier";
|
||||
|
||||
public RayOfPurification(){
|
||||
super("ray_of_purification", true, EnumAction.NONE);
|
||||
addProperties(DAMAGE, EFFECT_DURATION, BURN_DURATION, UNDEAD_DAMAGE_MULTIPLIER);
|
||||
}
|
||||
|
||||
// The following three methods serve as a good example of how to implement continuous spell sounds (hint: it's easy)
|
||||
|
||||
@Override
|
||||
protected SoundEvent[] createSounds(){
|
||||
return this.createContinuousSpellSounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, EntityLivingBase entity, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, entity, ticksInUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, double x, double y, double z, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, x, y, z, ticksInUse, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target)){
|
||||
|
||||
if(MagicDamage.isEntityImmune(DamageType.RADIANT, target)){
|
||||
if(!world.isRemote && ticksInUse == 1 && caster instanceof EntityPlayer) ((EntityPlayer)caster)
|
||||
.sendStatusMessage(new TextComponentTranslation("spell.resist", target.getName(),
|
||||
this.getNameForTranslationFormatted()), true);
|
||||
}else{
|
||||
|
||||
float damage = getProperty(DAMAGE).floatValue() * modifiers.get(SpellModifiers.POTENCY);
|
||||
// Fire
|
||||
if(((EntityLivingBase)target).isEntityUndead()){
|
||||
target.setFire((int)(getProperty(BURN_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
damage *= getProperty(UNDEAD_DAMAGE_MULTIPLIER).floatValue();
|
||||
}
|
||||
// Damage
|
||||
WizardryUtilities.attackEntityWithoutKnockback(target,
|
||||
MagicDamage.causeDirectMagicDamage(caster, DamageType.RADIANT), damage);
|
||||
// Blindness
|
||||
((EntityLivingBase)target).addPotionEffect(new PotionEffect(MobEffects.BLINDNESS,
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade))));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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 true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void spawnParticleRay(World world, Vec3d origin, Vec3d direction, EntityLivingBase caster, double distance){
|
||||
|
||||
if(caster != null){
|
||||
ParticleBuilder.create(Type.BEAM).entity(caster).pos(origin.subtract(caster.getPositionVector()))
|
||||
.length(distance).clr(1, 0.6f + 0.3f * world.rand.nextFloat(), 0.2f)
|
||||
.scale(MathHelper.sin(world.getTotalWorldTime() * 0.2f) * 0.1f + 1.4f).spawn(world);
|
||||
}else{
|
||||
ParticleBuilder.create(Type.BEAM).pos(origin).target(origin.add(direction.scale(distance)))
|
||||
.clr(1, 0.6f + 0.3f * world.rand.nextFloat(), 0.2f)
|
||||
.scale(MathHelper.sin(world.getTotalWorldTime() * 0.2f) * 0.1f + 1.4f).spawn(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.potion.Curse;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class RemoveCurse extends SpellBuff {
|
||||
|
||||
public RemoveCurse(){
|
||||
super("remove_curse", 1, 1, 0.3f);
|
||||
this.soundValues(0.7f, 1.2f, 0.4f);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean applyEffects(EntityLivingBase caster, SpellModifiers modifiers){
|
||||
|
||||
if(!caster.getActivePotionEffects().isEmpty()){
|
||||
|
||||
boolean flag = false;
|
||||
|
||||
for(PotionEffect effect : caster.getActivePotionEffects()){
|
||||
// The PotionEffect version (as opposed to Potion) does not call cleanup callbacks
|
||||
if(effect.getPotion() instanceof Curse){
|
||||
caster.removePotionEffect(effect.getPotion());
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void spawnParticles(World world, EntityLivingBase caster, SpellModifiers modifiers){
|
||||
|
||||
super.spawnParticles(world, caster, modifiers);
|
||||
|
||||
for(int i = 0; i < particleCount*2; i++){
|
||||
double x = caster.posX + world.rand.nextDouble() * 2 - 1;
|
||||
double y = caster.getEntityBoundingBox().minY + caster.getEyeHeight() - 0.5 + world.rand.nextDouble();
|
||||
double z = caster.posZ + world.rand.nextDouble() * 2 - 1;
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).vel(0, 0.14, 0).clr(0x0f001b)
|
||||
.time(20 + world.rand.nextInt(12)).spawn(world);
|
||||
ParticleBuilder.create(Type.DARK_MAGIC).pos(x, y, z).clr(0x0f001b).spawn(world);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,5 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -12,9 +8,13 @@ import net.minecraft.world.World;
|
||||
|
||||
public class ReplenishHunger extends SpellBuff {
|
||||
|
||||
public static final String HUNGER_POINTS = "hunger_points";
|
||||
public static final String SATURATION_MODIFIER = "saturation_modifier";
|
||||
|
||||
public ReplenishHunger(){
|
||||
super("replenish_hunger", Tier.APPRENTICE, Element.HEALING, SpellType.BUFF, 10, 30, WizardrySounds.SPELL_HEAL, 1, 0.7f, 0.3f);
|
||||
super("replenish_hunger", 1, 0.7f, 0.3f);
|
||||
this.soundValues(0.7f, 1.2f, 0.4f);
|
||||
addProperties(HUNGER_POINTS, SATURATION_MODIFIER);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return false; }
|
||||
@@ -28,9 +28,9 @@ public class ReplenishHunger extends SpellBuff {
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(caster.getFoodStats().needFood()){
|
||||
int foodAmount = (int)(4 * modifiers.get(SpellModifiers.POTENCY));
|
||||
int foodAmount = (int)(getProperty(HUNGER_POINTS).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
// Fixed issue #6: Changed to addStats, since setFoodLevel is client-side only
|
||||
caster.getFoodStats().addStats(foodAmount, foodAmount * 0.1f);
|
||||
caster.getFoodStats().addStats(foodAmount, getProperty(SATURATION_MODIFIER).floatValue());
|
||||
return super.cast(world, caster, hand, ticksInUse, modifiers);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.item.ISpellCastingItem;
|
||||
import electroblob.wizardry.packet.PacketResurrection;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class Resurrection extends Spell {
|
||||
|
||||
public static final String WAIT_TIME = "wait_time";
|
||||
|
||||
public Resurrection(){
|
||||
super("resurrection", EnumAction.NONE, false);
|
||||
addProperties(EFFECT_RADIUS, WAIT_TIME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresPacket(){
|
||||
return false; // Has its own packets
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
WizardData data = WizardData.get(caster);
|
||||
|
||||
double radius = getProperty(EFFECT_RADIUS).doubleValue() * modifiers.get(WizardryItems.range_upgrade);
|
||||
|
||||
if(!world.isRemote && caster.getServer() != null){
|
||||
// Potency reduces the time you have to wait to resurrect an ally
|
||||
int waitTime = (int)(getProperty(WAIT_TIME).floatValue() / modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
EntityPlayerMP nearestDeadAlly = caster.getServer().getPlayerList().getPlayers().stream()
|
||||
.filter(p -> !p.isEntityAlive() && p.deathTime > waitTime && (data.isPlayerAlly(p) || caster == p)
|
||||
&& p.getDistanceSq(caster) < radius * radius)
|
||||
.min(Comparator.comparingDouble(caster::getDistanceSq))
|
||||
.orElse(null);
|
||||
|
||||
if(nearestDeadAlly != null){
|
||||
// When the player entity dies, it is removed from world#loadedEntityList. However, it is NOT removed
|
||||
// from playerEntityList (and probably a few other places) until respawn is clicked, and since that
|
||||
// never happens here we need to clean up those references or the player will have duplicate entries
|
||||
// in some entity lists - and weirdness will ensue!
|
||||
world.removeEntity(nearestDeadAlly); // Clean up the old entity references
|
||||
resurrect(nearestDeadAlly); // Reset isDead, must be before spawning the player again
|
||||
world.spawnEntity(nearestDeadAlly); // Re-add the player to all the relevant entity lists
|
||||
|
||||
// Notify clients to reset the appropriate fields, spawn particles and play sounds
|
||||
IMessage msg = new PacketResurrection.Message(nearestDeadAlly.getEntityId());
|
||||
WizardryPacketHandler.net.sendToDimension(msg, caster.dimension);
|
||||
|
||||
if(caster == nearestDeadAlly){
|
||||
caster.getServer().getPlayerList().sendMessage(new TextComponentTranslation(
|
||||
"spell." + this.getRegistryName() + ".resurrect_self", caster.getDisplayName()));
|
||||
}else{
|
||||
caster.getServer().getPlayerList().sendMessage(new TextComponentTranslation(
|
||||
"spell." + this.getRegistryName() + ".resurrect_ally", nearestDeadAlly.getDisplayName(), caster.getDisplayName()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Sets the given player back to alive, sets their health to half-full and (on the client) spawns particles. */
|
||||
public void resurrect(EntityPlayer player){
|
||||
|
||||
player.isDead = false;
|
||||
player.setHealth(player.getMaxHealth() / 2);
|
||||
// Experience doesn't normally get reset until respawn, so we need to do that here too
|
||||
player.deathTime = 0;
|
||||
player.experience = 0;
|
||||
player.experienceLevel = 0;
|
||||
player.experienceTotal = 0;
|
||||
|
||||
if(player.world.isRemote){
|
||||
ParticleBuilder.spawnHealParticles(player.world, player);
|
||||
this.playSound(player.world, player, 0, -1, null); // We know the modifiers parameter isn't used
|
||||
}
|
||||
}
|
||||
|
||||
public static int getRemainingWaitTime(int timeSinceDeath){
|
||||
return Math.max(0, MathHelper.ceil((Spells.resurrection.getProperty(Resurrection.WAIT_TIME).floatValue() - timeSinceDeath) / 20));
|
||||
}
|
||||
|
||||
/** Helper method for detecting if a stack can be used to cast the resurrection spell. */
|
||||
public static boolean canStackResurrect(ItemStack stack, EntityPlayer player){
|
||||
return stack.getItem() instanceof ISpellCastingItem
|
||||
&& Arrays.asList(((ISpellCastingItem)stack.getItem()).getSpells(stack)).contains(Spells.resurrection)
|
||||
&& ((ISpellCastingItem)stack.getItem()).canCast(stack, Spells.resurrection, player, EnumHand.MAIN_HAND, 0, new SpellModifiers());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class Reversal extends SpellRay {
|
||||
|
||||
public static final String REVERSED_EFFECTS = "reversed_effects";
|
||||
|
||||
public Reversal(){
|
||||
super("reversal", false, EnumAction.NONE);
|
||||
addProperties(REVERSED_EFFECTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByDispensers(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, @Nullable EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
// Naturally, this spell won't work unless it has a living caster and target
|
||||
if(caster != null && target instanceof EntityLivingBase){
|
||||
|
||||
List<PotionEffect> negativePotions = new ArrayList<>(caster.getActivePotionEffects());
|
||||
negativePotions.removeIf(p -> !p.getPotion().isBadEffect());
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
if(negativePotions.isEmpty()) return false; // Needs potion effects to reverse!
|
||||
|
||||
// 1 effect for non-necromancy wands, 2 for apprentice necromancy wands, 3 for advanced and 4 for master
|
||||
int bonusEffects = (int)((modifiers.get(SpellModifiers.POTENCY) - 1) / Constants.POTENCY_INCREASE_PER_TIER + 0.5f) - 1;
|
||||
int n = getProperty(REVERSED_EFFECTS).intValue() + bonusEffects;
|
||||
|
||||
// Chooses n random negative potion effects, where n is the potency level
|
||||
Collections.shuffle(negativePotions);
|
||||
negativePotions = negativePotions.subList(0, negativePotions.size() < n ? negativePotions.size() : n);
|
||||
|
||||
// Now reverse them!
|
||||
negativePotions.forEach(p -> caster.removePotionEffect(p.getPotion()));
|
||||
negativePotions.forEach(((EntityLivingBase)target)::addPotionEffect);
|
||||
|
||||
}else{
|
||||
ParticleBuilder.create(Type.BUFF).entity(caster).clr(1, 1, 0.3f).spawn(world);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, @Nullable EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, @Nullable EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void spawnParticle(World world, double x, double y, double z, double vx, double vy, double vz){
|
||||
ParticleBuilder.create(Type.DARK_MAGIC).pos(x, y, z).clr(0.1f, 0, 0).spawn(world);
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(x, y, z).time(12 + world.rand.nextInt(8)).clr(0.1f, 0, 0.05f).spawn(world);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Satiety extends SpellBuff {
|
||||
|
||||
public static final String HUNGER_POINTS = "hunger_points";
|
||||
public static final String SATURATION_MODIFIER = "saturation_modifier";
|
||||
|
||||
public Satiety(){
|
||||
super("satiety", 1, 0.7f, 0.3f);
|
||||
this.soundValues(0.7f, 1.2f, 0.4f);
|
||||
addProperties(HUNGER_POINTS, SATURATION_MODIFIER);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return false; }
|
||||
|
||||
@Override
|
||||
protected boolean applyEffects(EntityLivingBase caster, SpellModifiers modifiers){
|
||||
return true; // In this case the best solution is to remove the functionality of this method and override cast.
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(caster.getFoodStats().needFood()){
|
||||
int foodAmount = (int)(getProperty(HUNGER_POINTS).floatValue() * modifiers.get(SpellModifiers.POTENCY));
|
||||
// Fixed issue #6: Changed to addStats, since setFoodLevel is client-side only
|
||||
caster.getFoodStats().addStats(foodAmount, getProperty(SATURATION_MODIFIER).floatValue());
|
||||
return super.cast(world, caster, hand, ticksInUse, modifiers);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +1,20 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.item.ItemWand;
|
||||
import electroblob.wizardry.integration.DamageSafetyChecker;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.util.IElementalDamage;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingAttackEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
@@ -27,8 +23,27 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
@Mod.EventBusSubscriber
|
||||
public class ShadowWard extends Spell {
|
||||
|
||||
public static final String REFLECTED_FRACTION = "reflected_fraction";
|
||||
|
||||
public ShadowWard(){
|
||||
super("shadow_ward", Tier.ADVANCED, Element.NECROMANCY, SpellType.DEFENCE, 10, 0, EnumAction.BLOCK, true);
|
||||
super("shadow_ward", EnumAction.BLOCK, true);
|
||||
addProperties(REFLECTED_FRACTION);
|
||||
soundValues(0.6f, 1, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent[] createSounds(){
|
||||
return this.createContinuousSpellSounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, EntityLivingBase entity, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, entity, ticksInUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, double x, double y, double z, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, x, y, z, ticksInUse, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -42,7 +57,7 @@ public class ShadowWard extends Spell {
|
||||
}
|
||||
|
||||
if(ticksInUse % 50 == 0){
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_PORTAL_AMBIENT, 0.6f, 1.5f);
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -50,22 +65,24 @@ public class ShadowWard extends Spell {
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingAttackEvent(LivingAttackEvent event){
|
||||
|
||||
if(event.getSource() != null && event.getSource().getTrueSource() instanceof EntityLivingBase){
|
||||
// There used to be a check that the target was a player here, but I don't see any reason for it.
|
||||
ItemStack wand = event.getEntityLiving().getActiveItemStack();
|
||||
|
||||
if(wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand
|
||||
&& WandHelper.getCurrentSpell(wand) instanceof ShadowWard && !event.getSource().isUnblockable()
|
||||
if(WizardryUtilities.isCasting(event.getEntityLiving(), Spells.shadow_ward) && !event.getSource().isUnblockable()
|
||||
&& !(event.getSource() instanceof IElementalDamage && ((IElementalDamage)event.getSource()).isRetaliatory())){
|
||||
|
||||
event.setCanceled(true);
|
||||
|
||||
float reflectedFraction = MathHelper.clamp(Spells.shadow_ward.getProperty(REFLECTED_FRACTION).floatValue(), 0, 1);
|
||||
|
||||
// Now we can preserve the original damage source (sort of) as long as we make it retaliatory.
|
||||
// For some reason this isn't working, so I've reverted to plain old magic damage for now.
|
||||
//event.getEntityLiving().attackEntityFrom(
|
||||
// MagicDamage.causeDirectMagicDamage(event.getSource().getTrueSource(), DamageType.MAGIC, true), event.getAmount() * 0.5f);
|
||||
DamageSafetyChecker.attackEntitySafely(event.getEntity(), DamageSource.MAGIC, event.getAmount() * 0.5f, event.getSource().getDamageType());
|
||||
((EntityLivingBase)event.getSource().getTrueSource()).attackEntityFrom(
|
||||
MagicDamage.causeDirectMagicDamage(event.getEntityLiving(), DamageType.MAGIC, true), event.getAmount() * 0.5f);
|
||||
DamageSafetyChecker.attackEntitySafely(event.getEntity(), DamageSource.MAGIC, event.getAmount()
|
||||
* (1 - reflectedFraction), event.getSource().getDamageType());
|
||||
event.getSource().getTrueSource().attackEntityFrom(MagicDamage.causeDirectMagicDamage(
|
||||
event.getEntityLiving(), DamageType.MAGIC, true), event.getAmount() * reflectedFraction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,40 +1,60 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.data.IVariable;
|
||||
import electroblob.wizardry.data.Persistence;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.entity.EntityShield;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundEvent;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Shield extends Spell {
|
||||
|
||||
public static final IVariable<EntityShield> SHIELD_KEY = new IVariable.Variable<>(Persistence.NEVER);
|
||||
|
||||
public Shield(){
|
||||
super("shield", Tier.APPRENTICE, Element.HEALING, SpellType.DEFENCE, 5, 0, EnumAction.BLOCK, true);
|
||||
super("shield", EnumAction.BLOCK, true);
|
||||
addProperties(EFFECT_STRENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected SoundEvent[] createSounds(){
|
||||
return this.createContinuousSpellSounds();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, EntityLivingBase entity, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, entity, ticksInUse);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void playSound(World world, double x, double y, double z, int ticksInUse, int duration, SpellModifiers modifiers, String... sounds){
|
||||
this.playSoundLoop(world, x, y, z, ticksInUse, duration);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
caster.addPotionEffect(new PotionEffect(MobEffects.RESISTANCE, 10, 0, false, false));
|
||||
caster.addPotionEffect(new PotionEffect(MobEffects.RESISTANCE, 10,
|
||||
getProperty(EFFECT_STRENGTH).intValue(), false, false));
|
||||
|
||||
if(WizardData.get(caster).shield == null){
|
||||
WizardData.get(caster).shield = new EntityShield(world, caster);
|
||||
if(WizardData.get(caster).getVariable(SHIELD_KEY) == null){
|
||||
|
||||
EntityShield shield = new EntityShield(world, caster);
|
||||
|
||||
WizardData.get(caster).setVariable(SHIELD_KEY, shield);
|
||||
if(!world.isRemote){
|
||||
world.spawnEntity(WizardData.get(caster).shield);
|
||||
world.spawnEntity(shield);
|
||||
}
|
||||
}
|
||||
if(ticksInUse == 0){
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,11 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.item.ItemArtefact;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.*;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -22,39 +15,59 @@ import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.network.play.server.SPacketEntityVelocity;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class Shockwave extends Spell {
|
||||
|
||||
private static final double BASE_RADIUS = 5;
|
||||
private static final float BASE_DAMAGE = 8;
|
||||
public static final String MAX_REPULSION_VELOCITY = "max_repulsion_velocity";
|
||||
/** The radius within which maximum damage is dealt and maximum repulsion velocity is applied. */
|
||||
private static final double EPICENTRE_RADIUS = 1;
|
||||
|
||||
public Shockwave(){
|
||||
super("shockwave", Tier.MASTER, Element.SORCERY, SpellType.ATTACK, 65, 150, EnumAction.BOW, false);
|
||||
super("shockwave", EnumAction.BOW, false);
|
||||
this.soundValues(2, 0.5f, 0);
|
||||
addProperties(BLAST_RADIUS, DAMAGE, MAX_REPULSION_VELOCITY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(
|
||||
BASE_RADIUS * modifiers.get(WizardryItems.blast_upgrade), caster.posX, caster.posY, caster.posZ, world);
|
||||
double radius = getProperty(BLAST_RADIUS).floatValue() * modifiers.get(WizardryItems.blast_upgrade);
|
||||
|
||||
List<EntityLivingBase> targets = WizardryUtilities.getEntitiesWithinRadius(radius, caster.posX, caster.posY, caster.posZ, world);
|
||||
|
||||
for(EntityLivingBase target : targets){
|
||||
if(WizardryUtilities.isValidTarget(caster, target)){
|
||||
|
||||
if(target instanceof EntityPlayer && (!Wizardry.settings.playersMoveEachOther
|
||||
|| ItemArtefact.isArtefactActive((EntityPlayer)target, WizardryItems.amulet_anchoring))){
|
||||
|
||||
if(!world.isRemote) caster.sendStatusMessage(new TextComponentTranslation("spell.resist",
|
||||
target.getName(), this.getNameForTranslationFormatted()), true);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(AllyDesignationSystem.isValidTarget(caster, target)){
|
||||
|
||||
// Produces a linear profile from 0 at the edge of the radius to 1 at the epicentre radius, then
|
||||
// a constant value of 1 within the epicentre radius.
|
||||
float proximity = (float)(1 - (Math.max(target.getDistance(caster) - EPICENTRE_RADIUS, 0))/(radius - EPICENTRE_RADIUS));
|
||||
|
||||
// Damage increases closer to player up to a maximum of 4 hearts (at 1 block distance).
|
||||
float damage = Math.min(BASE_DAMAGE / target.getDistance(caster), BASE_DAMAGE);
|
||||
target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(caster, DamageType.BLAST),
|
||||
damage * modifiers.get(SpellModifiers.POTENCY));
|
||||
getProperty(DAMAGE).floatValue() * proximity * modifiers.get(SpellModifiers.POTENCY));
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
// Entity speed increases closer to the player to a maximum of 3 (at 1 block distance).
|
||||
// This is the entity's speed compared to its distance from the player. Used for a similar triangles
|
||||
// based x, y and z speed calculation.
|
||||
double velocityFactor = Math.min(5 / target.getDistanceSq(caster), 3.0d);
|
||||
double velocityFactor = proximity * getProperty(MAX_REPULSION_VELOCITY).floatValue();
|
||||
|
||||
double dx = target.posX - caster.posX;
|
||||
double dy = target.posY + 1 - caster.posY;
|
||||
double dy = target.getEntityBoundingBox().minY + 1 - caster.posY;
|
||||
double dz = target.posZ - caster.posZ;
|
||||
|
||||
target.motionX = velocityFactor * dx;
|
||||
@@ -74,17 +87,17 @@ public class Shockwave extends Spell {
|
||||
double particleX, particleZ;
|
||||
|
||||
for(int i = 0; i < 40; i++){
|
||||
|
||||
particleX = caster.posX - 1.0d + 2 * world.rand.nextDouble();
|
||||
particleZ = caster.posZ - 1.0d + 2 * world.rand.nextDouble();
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(particleX, caster.getEntityBoundingBox().minY, particleZ)
|
||||
.vel(particleX - caster.posX, 0, particleZ - caster.posZ).time(30).clr(0.8f, 0.8f, 1).spawn(world);
|
||||
|
||||
particleX = caster.posX - 1.0d + 2 * world.rand.nextDouble();
|
||||
particleZ = caster.posZ - 1.0d + 2 * world.rand.nextDouble();
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(particleX, caster.getEntityBoundingBox().minY, particleZ)
|
||||
.vel(particleX - caster.posX, 0, particleZ - caster.posZ).time(30).clr(0.9f, 0.9f, 0.9f).spawn(world);
|
||||
|
||||
|
||||
// particleX = caster.posX - 1.0d + 2 * world.rand.nextDouble();
|
||||
// particleZ = caster.posZ - 1.0d + 2 * world.rand.nextDouble();
|
||||
// ParticleBuilder.create(Type.SPARKLE).pos(particleX, caster.getEntityBoundingBox().minY, particleZ)
|
||||
// .vel(particleX - caster.posX, 0, particleZ - caster.posZ).time(30).clr(0.8f, 0.8f, 1).spawn(world);
|
||||
//
|
||||
// particleX = caster.posX - 1.0d + 2 * world.rand.nextDouble();
|
||||
// particleZ = caster.posZ - 1.0d + 2 * world.rand.nextDouble();
|
||||
// ParticleBuilder.create(Type.SPARKLE).pos(particleX, caster.getEntityBoundingBox().minY, particleZ)
|
||||
// .vel(particleX - caster.posX, 0, particleZ - caster.posZ).time(30).clr(0.9f, 0.9f, 0.9f).spawn(world);
|
||||
|
||||
particleX = caster.posX - 1.0d + 2 * world.rand.nextDouble();
|
||||
particleZ = caster.posZ - 1.0d + 2 * world.rand.nextDouble();
|
||||
IBlockState block = WizardryUtilities.getBlockEntityIsStandingOn(caster);
|
||||
@@ -95,14 +108,19 @@ public class Shockwave extends Spell {
|
||||
}
|
||||
}
|
||||
|
||||
ParticleBuilder.create(Type.SPHERE)
|
||||
.pos(caster.posX, caster.getEntityBoundingBox().minY + 0.1, caster.posZ)
|
||||
.scale((float)radius * 0.8f)
|
||||
.clr(0.8f, 0.9f, 1)
|
||||
.spawn(world);
|
||||
|
||||
world.spawnParticle(EnumParticleTypes.EXPLOSION_LARGE, caster.posX,
|
||||
caster.getEntityBoundingBox().minY + 0.1, caster.posZ, 0, 0, 0);
|
||||
|
||||
}
|
||||
|
||||
caster.swingArm(hand);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SHOCKWAVE, 1.0f, 0.7f);
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_SHOCKWAVE, 2.0f, 0.3f);
|
||||
playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.item.EntityArmorStand;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntityShulkerBullet;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.nbt.NBTUtil;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
|
||||
public class ShulkerBullet extends Spell {
|
||||
|
||||
public ShulkerBullet(){
|
||||
super("shulker_bullet", EnumAction.NONE, false);
|
||||
this.soundValues(2, 1, 0.3f);
|
||||
addProperties(RANGE);
|
||||
}
|
||||
|
||||
@Override public boolean canBeCastByNPCs(){ return true; }
|
||||
|
||||
@Override public boolean canBeCastByDispensers(){ return true; }
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
if(!shoot(world, caster, caster.posX, caster.posY, caster.posZ, EnumFacing.UP, modifiers)) return false;
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){
|
||||
if(!shoot(world, caster, caster.posX, caster.posY, caster.posZ, EnumFacing.UP, modifiers)) return false;
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, double x, double y, double z, EnumFacing direction, int duration, int ticksInUse, SpellModifiers modifiers){
|
||||
if(!shoot(world, null, x, y, z, direction, modifiers)) return false;
|
||||
this.playSound(world, x, y, z, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean shoot(World world, @Nullable EntityLivingBase caster, double x, double y, double z, EnumFacing direction, SpellModifiers modifiers){
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
double range = getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade);
|
||||
|
||||
List<EntityLivingBase> possibleTargets = WizardryUtilities.getEntitiesWithinRadius(range, x, y, z, world);
|
||||
|
||||
possibleTargets.remove(caster);
|
||||
possibleTargets.removeIf(t -> t instanceof EntityArmorStand);
|
||||
|
||||
if(possibleTargets.isEmpty()) return false;
|
||||
|
||||
// getDistanceSq doesn't require square-rooting so it's faster when only comparing
|
||||
possibleTargets.sort(Comparator.comparingDouble(t -> t.getDistanceSq(x, y, z)));
|
||||
|
||||
Entity target = possibleTargets.get(0);
|
||||
|
||||
// Y axis because the player is always upright
|
||||
if(caster != null){
|
||||
world.spawnEntity(new EntityShulkerBullet(world, caster, target, direction.getAxis()));
|
||||
}else{
|
||||
// Can't use the normal constructor because doesn't accept null for the owner
|
||||
EntityShulkerBullet bullet = new EntityShulkerBullet(world);
|
||||
bullet.setLocationAndAngles(x, y, z, bullet.rotationYaw, bullet.rotationPitch);
|
||||
|
||||
// Where there's a will there's a way...
|
||||
NBTTagCompound nbt = new NBTTagCompound();
|
||||
bullet.writeToNBT(nbt);
|
||||
nbt.setInteger("Dir", direction.getIndex());
|
||||
BlockPos pos = new BlockPos(target);
|
||||
NBTTagCompound targetTag = NBTUtil.createUUIDTag(target.getUniqueID());
|
||||
targetTag.setInteger("X", pos.getX());
|
||||
targetTag.setInteger("Y", pos.getY());
|
||||
targetTag.setInteger("Z", pos.getZ());
|
||||
nbt.setTag("Target", targetTag);
|
||||
bullet.readFromNBT(nbt); // LOL I just modified private fields without reflection
|
||||
|
||||
world.spawnEntity(bullet);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -1,43 +1,60 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.PotionEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class SixthSense extends Spell {
|
||||
|
||||
/** A {@code ResourceLocation} representing the shader file used when under the effects of sixth sense. */
|
||||
public static final ResourceLocation SHADER = new ResourceLocation(Wizardry.MODID, "shaders/post/sixth_sense.json");
|
||||
|
||||
public SixthSense(){
|
||||
super("sixth_sense", Tier.APPRENTICE, Element.EARTH, SpellType.BUFF, 20, 100, EnumAction.BOW, false);
|
||||
super("sixth_sense", EnumAction.BOW, false);
|
||||
addProperties(EFFECT_DURATION, EFFECT_RADIUS);
|
||||
soundValues(1, 1.1f, 0.2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesSpellRequirePacket(){
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
// Cannot be cast when it has already been cast
|
||||
if(!world.isRemote){
|
||||
caster.addPotionEffect(new PotionEffect(WizardryPotions.sixth_sense,
|
||||
(int)(400 * modifiers.get(WizardryItems.duration_upgrade)),
|
||||
(int)((modifiers.get(WizardryItems.range_upgrade) - 1f) / Constants.RANGE_INCREASE_PER_LEVEL)));
|
||||
caster.addPotionEffect(new PotionEffect(WizardryPotions.sixth_sense,
|
||||
(int)(getProperty(EFFECT_DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)),
|
||||
(int)((modifiers.get(WizardryItems.range_upgrade) - 1f) / Constants.RANGE_INCREASE_PER_LEVEL)));
|
||||
|
||||
if(world.isRemote && caster == net.minecraft.client.Minecraft.getMinecraft().player){
|
||||
if(Wizardry.settings.useShaders) net.minecraft.client.Minecraft.getMinecraft().entityRenderer.loadShader(SHADER);
|
||||
electroblob.wizardry.client.WizardryClientEventHandler.playBlinkEffect();
|
||||
}
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SHOOT, 1.0F,
|
||||
world.rand.nextFloat() * 0.2F + 1.0F);
|
||||
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPotionAddedEvent(PotionEvent.PotionAddedEvent event){
|
||||
if(event.getEntity().world.isRemote && event.getPotionEffect().getPotion() == WizardryPotions.sixth_sense
|
||||
&& event.getEntity() == net.minecraft.client.Minecraft.getMinecraft().player){
|
||||
if(Wizardry.settings.useShaders) net.minecraft.client.Minecraft.getMinecraft().entityRenderer.loadShader(SHADER);
|
||||
electroblob.wizardry.client.WizardryClientEventHandler.playBlinkEffect();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,55 +1,32 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.entity.living.EntityMagicSlime;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.monster.EntitySkeleton;
|
||||
import net.minecraft.entity.monster.EntitySlime;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.EnumParticleTypes;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Slime extends SpellRay {
|
||||
|
||||
private static final int BASE_DURATION = 200;
|
||||
|
||||
public Slime(){
|
||||
super("slime", Tier.ADVANCED, Element.EARTH, SpellType.ATTACK, 20, 50, false, 8, WizardrySounds.SPELL_ICE);
|
||||
super("slime", false, EnumAction.NONE);
|
||||
addProperties(DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
// This spell uses more than one sound, so this is required...
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, modifiers);
|
||||
if(flag) WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SLIME_ATTACK, 1.0F, 0.5F);
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityLiving caster, EnumHand hand, int ticksInUse, EntityLivingBase target, SpellModifiers modifiers){
|
||||
boolean flag = super.cast(world, caster, hand, ticksInUse, target, modifiers);
|
||||
if(flag) caster.playSound(SoundEvents.ENTITY_SLIME_ATTACK, 1.0F, 0.5F);
|
||||
return flag;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(WizardryUtilities.isLiving(target) && !(target instanceof EntityMagicSlime)){
|
||||
|
||||
@@ -58,29 +35,24 @@ public class Slime extends SpellRay {
|
||||
new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted()), true);
|
||||
}else{
|
||||
|
||||
if(target instanceof EntitySkeleton && caster instanceof EntityPlayer)
|
||||
WizardryAdvancementTriggers.slime_skeleton.triggerFor((EntityPlayer)caster);
|
||||
|
||||
if(!world.isRemote){
|
||||
EntityMagicSlime slime = new EntityMagicSlime(world, caster, (EntityLivingBase)target,
|
||||
(int)(BASE_DURATION * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
(int)(getProperty(DURATION).floatValue() * modifiers.get(WizardryItems.duration_upgrade)));
|
||||
world.spawnEntity(slime);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@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 onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class SlowTime extends SpellBuff {
|
||||
|
||||
/** A {@code ResourceLocation} representing the shader file used when possessing an entity. */
|
||||
public static final ResourceLocation SHADER = new ResourceLocation(Wizardry.MODID, "shaders/post/slow_time.json");
|
||||
|
||||
public SlowTime(){
|
||||
super("slow_time", 0.2f, 0.8f, 0.8f, () -> WizardryPotions.slow_time);
|
||||
addProperties(EFFECT_RADIUS);
|
||||
soundValues(0.6f, 1.5f, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(caster.world.isRemote && caster == net.minecraft.client.Minecraft.getMinecraft().player){
|
||||
if(Wizardry.settings.useShaders) net.minecraft.client.Minecraft.getMinecraft().entityRenderer.loadShader(SHADER);
|
||||
}
|
||||
|
||||
return super.cast(world, caster, hand, ticksInUse, modifiers);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByDispensers(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canBeCastByNPCs(){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,5 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.tileentity.TileEntityPlayerSave;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
@@ -11,26 +8,28 @@ import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumFacing;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Snare extends SpellRay {
|
||||
|
||||
public Snare(){
|
||||
super("snare", Tier.BASIC, Element.EARTH, SpellType.ATTACK, 10, 10, false, 10, SoundEvents.BLOCK_GRASS_PLACE);
|
||||
super("snare", false, EnumAction.NONE);
|
||||
this.soundValues(1, 1.4f, 0.4f);
|
||||
this.ignoreEntities(true);
|
||||
this.ignoreLivingEntities(true);
|
||||
addProperties(DAMAGE, EFFECT_DURATION, EFFECT_STRENGTH);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onEntityHit(World world, Entity target, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onEntityHit(World world, Entity target, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(side == EnumFacing.UP && world.isSideSolid(pos, EnumFacing.UP)
|
||||
&& WizardryUtilities.canBlockBeReplaced(world, pos.up())){
|
||||
@@ -47,7 +46,7 @@ public class Snare extends SpellRay {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
|
||||
protected boolean onMiss(World world, EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers){
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.projectile.EntitySnowball;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumAction;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class Snowball extends Spell {
|
||||
|
||||
public Snowball(){
|
||||
super("snowball", Tier.BASIC, Element.ICE, SpellType.ATTACK, 1, 1, EnumAction.NONE, false);
|
||||
super("snowball", EnumAction.NONE, false);
|
||||
addProperties(RANGE);
|
||||
soundValues(0.5f, 0.4f, 0.2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesSpellRequirePacket(){
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -27,13 +26,18 @@ public class Snowball extends Spell {
|
||||
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
|
||||
|
||||
if(!world.isRemote){
|
||||
// Trajectory calculation - see SpellProjectile for a more detailed explanation
|
||||
float g = 0.03f;
|
||||
float launchHeight = caster.getEyeHeight();
|
||||
float range = getProperty(RANGE).floatValue() * modifiers.get(WizardryItems.range_upgrade);
|
||||
float velocity = MathHelper.sqrt(MathHelper.sqrt(g*g * (launchHeight*launchHeight + range*range)) - g*launchHeight);
|
||||
|
||||
EntitySnowball snowball = new EntitySnowball(world, caster);
|
||||
snowball.shoot(caster, caster.rotationPitch, caster.rotationYaw, 0.0f, 1.5f, 1.0f);
|
||||
snowball.shoot(caster, caster.rotationPitch, caster.rotationYaw, 0.0f, velocity, 1.0f);
|
||||
world.spawnEntity(snowball);
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F,
|
||||
0.4F / (world.rand.nextFloat() * 0.4F + 0.8F));
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
caster.swingArm(hand);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
package electroblob.wizardry.spell;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.SpellType;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryBlocks;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.tileentity.TileEntityTimer;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
@@ -19,13 +15,17 @@ import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class SpectralPathway extends Spell {
|
||||
|
||||
/** The base length of the conjured bridge, in blocks. */
|
||||
public static final String LENGTH = "length";
|
||||
|
||||
public SpectralPathway(){
|
||||
super("spectral_pathway", Tier.ADVANCED, Element.SORCERY, SpellType.UTILITY, 40, 300, EnumAction.BOW, false);
|
||||
super("spectral_pathway", EnumAction.BOW, false);
|
||||
addProperties(LENGTH, DURATION);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean doesSpellRequirePacket(){
|
||||
public boolean requiresPacket(){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -44,8 +44,6 @@ public class SpectralPathway extends Spell {
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
int baseLength = 15;
|
||||
|
||||
// Gets the coordinates of the nearest block intersection to the player's feet.
|
||||
// Remember that a block always takes the coordinates of its northwestern corner.
|
||||
BlockPos origin = new BlockPos(Math.round(caster.posX), (int)caster.getEntityBoundingBox().minY - 1,
|
||||
@@ -53,7 +51,7 @@ public class SpectralPathway extends Spell {
|
||||
|
||||
int startPoint = direction.getAxisDirection() == AxisDirection.POSITIVE ? -1 : 0;
|
||||
|
||||
for(int i = 0; i < (int)(baseLength * modifiers.get(WizardryItems.range_upgrade)); i++){
|
||||
for(int i = 0; i < (int)(getProperty(LENGTH).floatValue() * modifiers.get(WizardryItems.range_upgrade)); i++){
|
||||
// If either a block gets placed or one has already been placed, flag is set to true.
|
||||
flag = placePathwayBlockIfPossible(world, origin.offset(direction, startPoint + i),
|
||||
modifiers.get(WizardryItems.duration_upgrade)) || flag;
|
||||
@@ -63,17 +61,17 @@ public class SpectralPathway extends Spell {
|
||||
modifiers.get(WizardryItems.duration_upgrade)) || flag;
|
||||
}
|
||||
}
|
||||
// TODO: There may be some client/server discrepancies here.
|
||||
WizardryUtilities.playSoundAtPlayer(caster, WizardrySounds.SPELL_CONJURATION_LARGE, 1.0f, 1.0f);
|
||||
|
||||
this.playSound(world, caster, ticksInUse, -1, modifiers);
|
||||
|
||||
return flag;
|
||||
}
|
||||
|
||||
private static boolean placePathwayBlockIfPossible(World world, BlockPos pos, float durationMultiplier){
|
||||
if(WizardryUtilities.canBlockBeReplacedB(world, pos)){
|
||||
private boolean placePathwayBlockIfPossible(World world, BlockPos pos, float durationMultiplier){
|
||||
if(WizardryUtilities.canBlockBeReplaced(world, pos, true)){
|
||||
world.setBlockState(pos, WizardryBlocks.spectral_block.getDefaultState());
|
||||
if(world.getTileEntity(pos) instanceof TileEntityTimer){
|
||||
((TileEntityTimer)world.getTileEntity(pos)).setLifetime((int)(1200 * durationMultiplier));
|
||||
((TileEntityTimer)world.getTileEntity(pos)).setLifetime((int)(getProperty(DURATION).floatValue() * durationMultiplier));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user