Add flamecatcher spell, and addItemExtras hook for SpellConjuration

This commit is contained in:
Electroblob77
2020-10-21 22:23:54 +01:00
parent d85441867e
commit 2bb242e6fb
44 changed files with 622 additions and 6 deletions
@@ -766,6 +766,8 @@ public class ClientProxy extends CommonProxy {
new ResourceLocation(Wizardry.MODID, "textures/entity/dart.png"), false, 8.0, 2.0, 16, 5, true));
RenderingRegistry.registerEntityRenderingHandler(EntityIceLance.class, manager -> new RenderMagicArrow(manager,
new ResourceLocation(Wizardry.MODID, "textures/entity/ice_lance.png"), false, 16.0, 3.0, 22, 5, true));
RenderingRegistry.registerEntityRenderingHandler(EntityFlamecatcherArrow.class, manager -> new RenderMagicArrow(manager,
new ResourceLocation(Wizardry.MODID, "textures/entity/flamecatcher_arrow.png"), false, 8, 2.0, 16, 5, true));
RenderingRegistry.registerEntityRenderingHandler(EntityForceArrow.class, RenderForceArrow::new);
@@ -4,6 +4,7 @@ import electroblob.wizardry.client.renderer.overlay.RenderBlinkEffect;
import electroblob.wizardry.data.DispenserCastingData;
import electroblob.wizardry.data.SpellEmitterData;
import electroblob.wizardry.item.ItemArtefact;
import electroblob.wizardry.item.ItemFlamecatcher;
import electroblob.wizardry.item.ItemSpectralBow;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.potion.PotionSlowTime;
@@ -164,7 +165,9 @@ public final class WizardryClientEventHandler {
public static void onFOVUpdateEvent(FOVUpdateEvent event){
// Bow zoom. Taken directly from AbstractClientPlayer so it works exactly like vanilla.
if(event.getEntity().isHandActive() && event.getEntity().getActiveItemStack().getItem() instanceof ItemSpectralBow){
if(event.getEntity().isHandActive() &&
(event.getEntity().getActiveItemStack().getItem() instanceof ItemSpectralBow
|| event.getEntity().getActiveItemStack().getItem() instanceof ItemFlamecatcher)){
int maxUseTicks = event.getEntity().getItemInUseMaxCount();
@@ -0,0 +1,71 @@
package electroblob.wizardry.entity.projectile;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.ParticleBuilder;
import electroblob.wizardry.util.ParticleBuilder.Type;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class EntityFlamecatcherArrow extends EntityMagicArrow {
public static final float SPEED = 3;
/** Creates a new magic missile in the given world. */
public EntityFlamecatcherArrow(World world){
super(world);
}
@Override public double getDamage(){ return Spells.flamecatcher.getProperty(Spell.DAMAGE).floatValue(); }
@Override public int getLifetime(){ return (int)(Spells.flamecatcher.getProperty(Spell.RANGE).floatValue() / SPEED); }
@Override public boolean doGravity(){ return false; } // Zero gravity arrows!
@Override public boolean doDeceleration(){ return false; }
@Override
public void onEntityHit(EntityLivingBase entityHit){
entityHit.setFire(Spells.flamecatcher.getProperty(Spell.BURN_DURATION).intValue());
this.playSound(WizardrySounds.ENTITY_FLAMECATCHER_ARROW_HIT, 1.0F, 1.2F / (this.rand.nextFloat() * 0.2F + 0.9F));
if(this.world.isRemote) ParticleBuilder.create(Type.FLASH).pos(posX, posY, posZ).clr(0xff6d00).spawn(world);
}
@Override
public void onBlockHit(RayTraceResult hit){
if(this.world.isRemote){
// Gets a position slightly away from the block hit so the particle doesn't get cut in half by the block face
Vec3d vec = hit.hitVec.add(new Vec3d(hit.sideHit.getDirectionVec()).scale(0.15));
ParticleBuilder.create(Type.FLASH).pos(vec).clr(0xff6d00).fade(0.85f, 0.5f, 0.8f).spawn(world);
}
}
@Override
public void tickInAir(){
if(this.world.isRemote){
ParticleBuilder.create(Type.MAGIC_FIRE, rand, posX, posY, posZ, 0.03, false)
.time(20 + rand.nextInt(10)).spawn(world);
if(this.ticksExisted > 1){ // Don't spawn particles behind where it started!
double x = posX - motionX / 2;
double y = posY - motionY / 2;
double z = posZ - motionZ / 2;
ParticleBuilder.create(Type.MAGIC_FIRE, rand, x, y, z, 0.03, false)
.time(20 + rand.nextInt(10)).spawn(world);
}
}
}
@Override
protected void entityInit(){
if(world != null && world.isRemote){
ParticleBuilder.create(Type.FLASH).entity(this).time(this.getLifetime()).scale(1.5f).clr(0xffb800).spawn(world);
}
}
}
@@ -0,0 +1,220 @@
package electroblob.wizardry.item;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.entity.projectile.EntityFlamecatcherArrow;
import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.spell.Flamecatcher;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumRarity;
import net.minecraft.item.IItemPropertyGetter;
import net.minecraft.item.ItemBow;
import net.minecraft.item.ItemStack;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import javax.annotation.Nullable;
public class ItemFlamecatcher extends ItemBow implements IConjuredItem {
private static final float DRAW_TIME = 10;
public ItemFlamecatcher(){
super();
setMaxDamage(1200);
setNoRepair();
setCreativeTab(null);
this.addPropertyOverride(new ResourceLocation("pull"), new IItemPropertyGetter(){
@SideOnly(Side.CLIENT)
public float apply(ItemStack stack, @Nullable World worldIn, @Nullable EntityLivingBase entityIn){
if(entityIn == null){
return 0.0F;
}else{
ItemStack itemstack = entityIn.getActiveItemStack();
return itemstack.getItem() == ItemFlamecatcher.this
? (float)(stack.getMaxItemUseDuration() - entityIn.getItemInUseCount()) / DRAW_TIME : 0;
}
}
});
this.addPropertyOverride(new ResourceLocation("pulling"), new IItemPropertyGetter(){
@SideOnly(Side.CLIENT)
public float apply(ItemStack stack, @Nullable World worldIn, @Nullable EntityLivingBase entityIn){
return entityIn != null && entityIn.isHandActive() && entityIn.getActiveItemStack() == stack ? 1 : 0;
}
});
addAnimationPropertyOverrides();
}
@Override
public EnumRarity getRarity(ItemStack stack){
return EnumRarity.EPIC;
}
@Override
@SideOnly(Side.CLIENT)
// Why does this still exist? Item models deal with this now, right?
public boolean isFull3D(){
return true;
}
@Override
public int getMaxDamage(ItemStack stack){
return this.getMaxDamageFromNBT(stack, Spells.flamecatcher);
}
@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)
// This code should only run on the client side, so using Minecraft is ok.
// Why the heck was this here?
//&& !net.minecraft.client.Minecraft.getMinecraft().player.isHandActive())
return false;
}
return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged);
}
// Copied fixes from ItemWand made possible by recently-added Forge hooks
@Override
public boolean canContinueUsing(ItemStack oldStack, ItemStack newStack){
// Ignore durability changes
if(ItemStack.areItemsEqualIgnoreDurability(oldStack, newStack)) return true;
return super.canContinueUsing(oldStack, newStack);
}
@Override
public boolean shouldCauseBlockBreakReset(ItemStack oldStack, ItemStack newStack){
// Ignore durability changes
if(ItemStack.areItemsEqualIgnoreDurability(oldStack, newStack)) return false;
return super.shouldCauseBlockBreakReset(oldStack, newStack);
}
@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);
}
// The following two methods re-route the displayed durability through the proxies in order to override the pausing
// of the item timer when the bow is being pulled.
@Override
public double getDurabilityForDisplay(ItemStack stack){
return Wizardry.proxy.getConjuredBowDurability(stack);
}
public double getDefaultDurabilityForDisplay(ItemStack stack){
return super.getDurabilityForDisplay(stack);
}
@Override
public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer player, EnumHand hand){
ItemStack stack = player.getHeldItem(hand);
int shotsLeft = stack.getTagCompound().getInteger(Flamecatcher.SHOTS_REMAINING_NBT_KEY);
if(shotsLeft == 0) return ActionResult.newResult(EnumActionResult.PASS, stack);
ActionResult<ItemStack> ret = net.minecraftforge.event.ForgeEventFactory.onArrowNock(stack, world, player, hand,
true);
if(ret != null) return ret;
player.setActiveHand(hand);
return ActionResult.newResult(EnumActionResult.SUCCESS, stack);
}
@Override
public boolean getIsRepairable(ItemStack stack, ItemStack stack2){
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;
}
// @Override
// public void onUsingTick(ItemStack stack, EntityLivingBase player, int count){
// // player.getItemInUseMaxCount() is named incorrectly; you only have to look at the method to see what it really
// // does.
// if(stack.getItemDamage() + player.getItemInUseMaxCount() > stack.getMaxDamage())
// player.replaceItemInInventory(player.getActiveHand() == EnumHand.MAIN_HAND ? 98 : 99, ItemStack.EMPTY);
// }
@Override
public void onPlayerStoppedUsing(ItemStack stack, World world, EntityLivingBase entity, int timeLeft){
// Decreases the timer by the amount it should have been decreased while the bow was in use.
if(!world.isRemote) stack.setItemDamage(stack.getItemDamage() + (this.getMaxItemUseDuration(stack) - timeLeft));
if(entity instanceof EntityPlayer){
EntityPlayer player = (EntityPlayer)entity;
int charge = this.getMaxItemUseDuration(stack) - timeLeft;
charge = net.minecraftforge.event.ForgeEventFactory.onArrowLoose(stack, world, (EntityPlayer)entity, charge, true);
if(charge < 0) return;
if(stack.getTagCompound() != null){
int shotsLeft = stack.getTagCompound().getInteger(Flamecatcher.SHOTS_REMAINING_NBT_KEY);
stack.getTagCompound().setInteger(Flamecatcher.SHOTS_REMAINING_NBT_KEY, shotsLeft - 1);
if(shotsLeft == 0 && !world.isRemote){
stack.setItemDamage(getMaxDamage(stack) - getAnimationFrames());
}
}
float velocity = (float)charge / DRAW_TIME;
velocity = (velocity * velocity + velocity * 2) / 3;
if(velocity > 1) velocity = 1;
if((double)velocity >= 0.1D){
if(!world.isRemote){
EntityFlamecatcherArrow arrow = new EntityFlamecatcherArrow(world);
arrow.aim(player, EntityFlamecatcherArrow.SPEED * velocity);
world.spawnEntity(arrow);
}
world.playSound(null, player.posX, player.posY, player.posZ,
WizardrySounds.ITEM_FLAMECATCHER_SHOOT, WizardrySounds.SPELLS, 1, 1);
world.playSound(null, player.posX, player.posY, player.posZ,
WizardrySounds.ITEM_FLAMECATCHER_FLAME, WizardrySounds.SPELLS, 1, 1);
}
}
}
}
@@ -253,6 +253,7 @@ public final class Spells {
public static final Spell radiant_totem = placeholder();
public static final Spell firestorm = placeholder();
public static final Spell flamecatcher = placeholder();
public static final Spell zombie_apocalypse = placeholder();
public static final Spell boulder = placeholder();
public static final Spell celestial_smite = placeholder();
@@ -472,6 +473,7 @@ public final class Spells {
registry.register(new RadiantTotem());
registry.register(new Firestorm());
registry.register(new Flamecatcher());
registry.register(new ZombieApocalypse());
registry.register(new Boulder());
registry.register(new CelestialSmite());
@@ -112,6 +112,7 @@ public class WizardryEntities {
registry.register(createEntry(EntityForceArrow.class, "force_arrow", TrackingType.PROJECTILE).build());
registry.register(createEntry(EntityDart.class, "dart", TrackingType.PROJECTILE).build());
registry.register(createEntry(EntityIceLance.class, "ice_lance", TrackingType.PROJECTILE).build());
registry.register(createEntry(EntityFlamecatcherArrow.class, "flamecatcher_arrow", TrackingType.PROJECTILE).build());
// Directionless projectiles
registry.register(createEntry(EntityFirebomb.class, "firebomb", TrackingType.PROJECTILE).build());
@@ -212,6 +212,7 @@ public final class WizardryItems {
public static final Item spectral_boots = placeholder();
public static final Item lightning_hammer = placeholder();
public static final Item flamecatcher = placeholder();
public static final Item ring_condensing = placeholder();
public static final Item ring_siphoning = placeholder();
@@ -583,6 +584,7 @@ public final class WizardryItems {
registerItem(registry, "spectral_boots", new ItemSpectralArmour(ArmorMaterial.IRON, 1, EntityEquipmentSlot.FEET));
registerItem(registry, "lightning_hammer", new ItemLightningHammer());
registerItem(registry, "flamecatcher", new ItemFlamecatcher());
registerItem(registry, "ring_condensing", new ItemArtefact(EnumRarity.RARE, ItemArtefact.Type.RING));
registerItem(registry, "ring_siphoning", new ItemArtefact(EnumRarity.UNCOMMON, ItemArtefact.Type.RING));
@@ -43,6 +43,8 @@ public final class WizardrySounds {
public static final SoundEvent ITEM_PURIFYING_ELIXIR_DRINK = createSound("item.purifying_elixir.drink");
public static final SoundEvent ITEM_MANA_FLASK_USE = createSound("item.mana_flask.use");
public static final SoundEvent ITEM_MANA_FLASK_RECHARGE = createSound("item.mana_flask.recharge");
public static final SoundEvent ITEM_FLAMECATCHER_SHOOT = createSound("item.flamecatcher.shoot");
public static final SoundEvent ITEM_FLAMECATCHER_FLAME = createSound("item.flamecatcher.flame");
public static final SoundEvent ENTITY_BLACK_HOLE_AMBIENT = createSound("entity.black_hole.ambient");
public static final SoundEvent ENTITY_BLACK_HOLE_VANISH = createSound("entity.black_hole.vanish");
@@ -124,6 +126,7 @@ public final class WizardrySounds {
public static final SoundEvent ENTITY_FIREBOMB_THROW = createSound("entity.firebomb.throw");
public static final SoundEvent ENTITY_FIREBOMB_SMASH = createSound("entity.firebomb.smash");
public static final SoundEvent ENTITY_FIREBOMB_FIRE = createSound("entity.firebomb.fire");
public static final SoundEvent ENTITY_FLAMECATCHER_ARROW_HIT = createSound("entity.flamecatcher_arrow.hit");
public static final SoundEvent ENTITY_FORCE_ARROW_HIT = createSound("entity.force_arrow.hit");
public static final SoundEvent ENTITY_FORCE_ORB_HIT = createSound("entity.force_orb.hit");
public static final SoundEvent ENTITY_FORCE_ORB_HIT_BLOCK = createSound("entity.force_orb.hit_block");
@@ -0,0 +1,43 @@
package electroblob.wizardry.spell;
import electroblob.wizardry.registry.WizardryItems;
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.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.world.World;
public class Flamecatcher extends SpellConjuration {
public static final String SHOT_COUNT = "shot_count";
public static final String SHOTS_REMAINING_NBT_KEY = "shotsRemaining";
public Flamecatcher(){
super("flamecatcher", WizardryItems.flamecatcher);
addProperties(RANGE, SHOT_COUNT, DAMAGE, BURN_DURATION);
}
@Override
protected void addItemExtras(EntityPlayer caster, ItemStack stack, SpellModifiers modifiers){
if(stack.getTagCompound() == null) stack.setTagCompound(new NBTTagCompound());
stack.getTagCompound().setInteger(SHOTS_REMAINING_NBT_KEY, (int)(getProperty(SHOT_COUNT).intValue() * modifiers.get(SpellModifiers.POTENCY)));
}
@Override
protected void spawnParticles(World world, EntityLivingBase caster, SpellModifiers modifiers){
ParticleBuilder.create(Type.BUFF).entity(caster).clr(0xff6d00).spawn(world);
for(int i=0; i<10; i++){
double x = caster.posX + world.rand.nextDouble() * 2 - 1;
double y = caster.posY + caster.getEyeHeight() - 0.5 + world.rand.nextDouble();
double z = caster.posZ + world.rand.nextDouble() * 2 - 1;
world.spawnParticle(EnumParticleTypes.FLAME, x, y, z, 0, 0, 0);
}
}
}
@@ -83,17 +83,29 @@ public class SpellConjuration extends Spell {
ItemStack stack = new ItemStack(item);
if(InventoryUtils.doesPlayerHaveItem(caster, item)) return false;
IConjuredItem.setDurationMultiplier(stack, modifiers.get(WizardryItems.duration_upgrade));
IConjuredItem.setDamageMultiplier(stack, modifiers.get(SpellModifiers.POTENCY));
if(InventoryUtils.doesPlayerHaveItem(caster, item)) return false;
addItemExtras(caster, stack, modifiers);
if(caster.getHeldItemMainhand().isEmpty()){
caster.setHeldItem(EnumHand.MAIN_HAND, stack);
return true;
}else{
return caster.inventory.addItemStackToInventory(stack);
if(!caster.inventory.addItemStackToInventory(stack)) return false;
}
return true;
}
/**
* Called directly <i>before</i> the conjured item is added to the inventory to perform additional behaviour (such
* as NBT modification). Does nothing by default.
* @param caster The player that cast this spell
* @param stack The item stack being conjured
* @param modifiers The modifiers this spell was cast with
*/
protected void addItemExtras(EntityPlayer caster, ItemStack stack, SpellModifiers modifiers){}
}