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:
@@ -0,0 +1,220 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import electroblob.wizardry.entity.construct.EntityHammer;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.spell.LightningHammer;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
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.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.item.EnumRarity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.EnumActionResult;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class ItemLightningHammer extends Item implements IConjuredItem {
|
||||
|
||||
public static final String DURATION_NBT_KEY = "duration";
|
||||
// Annoyingly we can't implement this for attack damage, but at least it gets saved for when the hammer is thrown
|
||||
public static final String DAMAGE_MULTIPLIER_NBT_KEY = "damageMultiplier";
|
||||
|
||||
public static final UUID MOVEMENT_SPEED_MODIFIER = UUID.fromString("d4c3bd93-c8e3-49c5-b35b-9356663bad1b");
|
||||
|
||||
private static final double ATTACK_SPEED = -3.2;
|
||||
private static final double CHAINING_RANGE = 4;
|
||||
private static final float CHAINING_DAMAGE = 4;
|
||||
private static final double THROW_SPEED = 0.75;
|
||||
private static final double MOVEMENT_SPEED_REDUCTION = -0.25;
|
||||
|
||||
public ItemLightningHammer(){
|
||||
super();
|
||||
setMaxDamage(600);
|
||||
setMaxStackSize(1);
|
||||
setNoRepair();
|
||||
setCreativeTab(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumRarity getRarity(ItemStack stack){
|
||||
return EnumRarity.EPIC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxDamage(ItemStack stack){
|
||||
if(stack.hasTagCompound() && stack.getTagCompound().hasKey(DURATION_NBT_KEY)){
|
||||
return stack.getTagCompound().getInteger(DURATION_NBT_KEY);
|
||||
}
|
||||
return super.getMaxDamage(stack);
|
||||
}
|
||||
|
||||
private float getDamageMultiplier(ItemStack stack){
|
||||
if(stack.hasTagCompound() && stack.getTagCompound().hasKey(DAMAGE_MULTIPLIER_NBT_KEY)){
|
||||
return stack.getTagCompound().getFloat(DAMAGE_MULTIPLIER_NBT_KEY);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Multimap<String, AttributeModifier> getItemAttributeModifiers(EntityEquipmentSlot slot){
|
||||
|
||||
Multimap<String, AttributeModifier> multimap = super.getItemAttributeModifiers(slot);
|
||||
|
||||
if(slot == EntityEquipmentSlot.MAINHAND){
|
||||
multimap.put(SharedMonsterAttributes.ATTACK_DAMAGE.getName(), new AttributeModifier(ATTACK_DAMAGE_MODIFIER, "Weapon modifier", Spells.lightning_hammer.getProperty(Spell.DIRECT_DAMAGE).floatValue(), WizardryUtilities.Operations.ADD));
|
||||
multimap.put(SharedMonsterAttributes.ATTACK_SPEED.getName(), new AttributeModifier(ATTACK_SPEED_MODIFIER, "Weapon modifier", ATTACK_SPEED, WizardryUtilities.Operations.ADD));
|
||||
multimap.put(SharedMonsterAttributes.MOVEMENT_SPEED.getName(), new AttributeModifier(MOVEMENT_SPEED_MODIFIER, "Weapon modifier", MOVEMENT_SPEED_REDUCTION, WizardryUtilities.Operations.MULTIPLY_FLAT));
|
||||
}
|
||||
|
||||
return multimap;
|
||||
}
|
||||
|
||||
@Override
|
||||
// This method allows the code for the item's timer to be greatly simplified by damaging it directly from
|
||||
// onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff.
|
||||
public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){
|
||||
|
||||
if(!oldStack.isEmpty() || !newStack.isEmpty()){
|
||||
// We only care about the situation where we specifically want the animation NOT to play.
|
||||
if(oldStack.getItem() == newStack.getItem() && !slotChanged) return false;
|
||||
}
|
||||
|
||||
return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean selected){
|
||||
int damage = stack.getItemDamage();
|
||||
if(damage > stack.getMaxDamage()) entity.replaceItemInInventory(slot, ItemStack.EMPTY);
|
||||
stack.setItemDamage(damage + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer player, EnumHand hand){
|
||||
|
||||
ItemStack stack = player.getHeldItem(hand);
|
||||
|
||||
if(!world.isRemote){
|
||||
EntityHammer hammer = new EntityHammer(world);
|
||||
Vec3d look = player.getLookVec();
|
||||
Vec3d vec = player.getPositionEyes(1).add(look);
|
||||
hammer.setPositionAndRotation(vec.x, vec.y - hammer.height/2, vec.z, player.rotationYawHead - 90, 0);
|
||||
// For some reason the above method insists on clamping the pitch to between -90 and 90
|
||||
hammer.rotationPitch = 180 + player.rotationPitch;
|
||||
hammer.prevRotationPitch = hammer.rotationPitch;
|
||||
|
||||
float attackStrength = player.getCooledAttackStrength(0);
|
||||
double speed = THROW_SPEED * attackStrength; // Throw distance depends on the attack meter
|
||||
hammer.addVelocity(look.x * speed, look.y * speed, look.z * speed);
|
||||
hammer.lifetime = stack.getMaxDamage() - stack.getItemDamage();
|
||||
hammer.setCaster(player);
|
||||
hammer.damageMultiplier = getDamageMultiplier(stack);
|
||||
hammer.spin = true;
|
||||
world.spawnEntity(hammer);
|
||||
}
|
||||
|
||||
WizardryUtilities.playSoundAtPlayer(player, WizardrySounds.ENTITY_HAMMER_THROW, 1.0F, 0.8f);
|
||||
|
||||
//player.swingArm(hand);
|
||||
|
||||
// Use this instead of stack.shrink so it works regardless of whether the player is in creative mode or not
|
||||
player.setHeldItem(hand, ItemStack.EMPTY);
|
||||
|
||||
return ActionResult.newResult(EnumActionResult.SUCCESS, stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemEnchantability(){
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnchantable(ItemStack stack){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBookEnchantable(ItemStack stack, ItemStack book){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cannot be dropped
|
||||
@Override
|
||||
public boolean onDroppedByPlayer(ItemStack item, EntityPlayer player){
|
||||
return false;
|
||||
}
|
||||
|
||||
// Can't be done in hitEntity because that's only called server-side, and after the cooldown is reset
|
||||
@SubscribeEvent
|
||||
public static void onAttackEntityEvent(AttackEntityEvent event){
|
||||
|
||||
ItemStack stack = event.getEntityPlayer().getHeldItemMainhand();
|
||||
|
||||
if(stack.getItem() instanceof ItemLightningHammer && event.getTarget() instanceof EntityLivingBase){
|
||||
|
||||
EntityPlayer wielder = event.getEntityPlayer();
|
||||
EntityLivingBase hit = (EntityLivingBase)event.getTarget();
|
||||
|
||||
float attackStrength = wielder.getCooledAttackStrength(0);
|
||||
|
||||
double dx = wielder.posX - hit.posX;
|
||||
double dz;
|
||||
for(dz = wielder.posZ - hit.posZ; dx * dx + dz * dz < 1.0E-4D; dz = (Math.random() - Math.random())
|
||||
* 0.01D){
|
||||
dx = (Math.random() - Math.random()) * 0.01D;
|
||||
}
|
||||
|
||||
hit.knockBack(wielder, 2 * attackStrength, dx, dz);
|
||||
|
||||
if(attackStrength == 1){ // Only chains when the attack meter is full
|
||||
|
||||
List<EntityLivingBase> nearby = WizardryUtilities.getEntitiesWithinRadius(CHAINING_RANGE, hit.posX, hit.posY, hit.posZ, hit.world);
|
||||
|
||||
nearby.remove(hit);
|
||||
nearby.remove(wielder);
|
||||
// When held, the number of chaining targets is halved
|
||||
int maxTargets = Spells.lightning_hammer.getProperty(LightningHammer.SECONDARY_MAX_TARGETS).intValue() / 2;
|
||||
while(nearby.size() > maxTargets) nearby.remove(nearby.size() - 1);
|
||||
|
||||
for(EntityLivingBase target : nearby){
|
||||
|
||||
target.attackEntityFrom(MagicDamage.causeDirectMagicDamage(wielder, DamageType.SHOCK), CHAINING_DAMAGE * ((ItemLightningHammer)stack.getItem()).getDamageMultiplier(stack));
|
||||
|
||||
if(hit.world.isRemote){
|
||||
ParticleBuilder.create(Type.LIGHTNING).pos(hit.getPositionVector().add(0, hit.height / 2, 0))
|
||||
.target(target).spawn(hit.world);
|
||||
ParticleBuilder.spawnShockParticles(hit.world, target.posX, target.getEntityBoundingBox().minY + target.height / 2, target.posZ);
|
||||
}
|
||||
|
||||
//target.playSound(WizardrySounds.SPELL_SPARK, 1, 1.5f + 0.4f * world.rand.nextFloat());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user