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,59 +1,118 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.spell.SpellConjuration;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.item.IItemPropertyGetter;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.item.ItemTossEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingDropsEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Allows wizardry to identify items that are conjured (and therefore need destroying if they leave the inventory)
|
||||
* without explicitly referencing each, thereby allowing for better expandibility.
|
||||
* without explicitly referencing each, thereby allowing for better expandability.
|
||||
*/
|
||||
@Mod.EventBusSubscriber
|
||||
public interface IConjuredItem {
|
||||
|
||||
/** The NBT tag key used to store the duration multiplier for conjured items. */
|
||||
public static final String DURATION_MULTIPLIER_KEY = "durationMultiplier";
|
||||
String DURATION_MULTIPLIER_KEY = "durationMultiplier";
|
||||
/** The NBT tag key used to store the damage multiplier for conjured items. */
|
||||
String DAMAGE_MULTIPLIER = "damageMultiplier";
|
||||
|
||||
UUID POTENCY_MODIFIER = UUID.fromString("da067ea6-0b35-4140-8436-5476224de9dd");
|
||||
|
||||
/** Helper method for setting the duration multiplier (via NBT) for conjured items. */
|
||||
public static void setDurationMultiplier(ItemStack stack, float multiplier){
|
||||
static void setDurationMultiplier(ItemStack stack, float multiplier){
|
||||
if(!stack.hasTagCompound()) stack.setTagCompound(new NBTTagCompound());
|
||||
stack.getTagCompound().setFloat(DURATION_MULTIPLIER_KEY, multiplier);
|
||||
}
|
||||
|
||||
/** Helper method for setting the damage multiplier (via NBT) for conjured items. */
|
||||
static void setDamageMultiplier(ItemStack stack, float multiplier){
|
||||
if(!stack.hasTagCompound()) stack.setTagCompound(new NBTTagCompound());
|
||||
stack.getTagCompound().setFloat(DAMAGE_MULTIPLIER, multiplier);
|
||||
}
|
||||
|
||||
/** Helper method for getting the damage multiplier (via NBT) for conjured items. */
|
||||
static float getDamageMultiplier(ItemStack stack){
|
||||
if(!stack.hasTagCompound()) return 1;
|
||||
return stack.getTagCompound().getFloat(DAMAGE_MULTIPLIER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for returning the max damage of a conjured item based on its NBT data. Centralises the code.
|
||||
* Implementors will almost certainly want to call this from {@link Item#getMaxDamage(ItemStack stack)}.
|
||||
*/
|
||||
public default int getMaxDamageFromNBT(ItemStack stack){
|
||||
default int getMaxDamageFromNBT(ItemStack stack, Spell spell){
|
||||
|
||||
float baseDuration = spell.getProperty(SpellConjuration.ITEM_LIFETIME).floatValue();
|
||||
|
||||
if(stack.hasTagCompound() && stack.getTagCompound().hasKey(DURATION_MULTIPLIER_KEY)){
|
||||
return (int)(this.getBaseDuration() * stack.getTagCompound().getFloat(DURATION_MULTIPLIER_KEY));
|
||||
return (int)(baseDuration * stack.getTagCompound().getFloat(DURATION_MULTIPLIER_KEY));
|
||||
}
|
||||
return this.getBaseDuration();
|
||||
|
||||
return (int)baseDuration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base duration in ticks for this conjured item. Should be a constant (commonly 600). Implementors may
|
||||
* want to call this when setting an item's max damage in its constructor.
|
||||
* Adds property overrides to define the conjuring/vanishing animation. Call this from the item's constructor.
|
||||
*/
|
||||
public int getBaseDuration();
|
||||
default void addAnimationPropertyOverrides(){
|
||||
|
||||
if(!(this instanceof Item)) throw new ClassCastException("Cannot set up conjuring animations for a non-item!");
|
||||
|
||||
Item item = (Item)this;
|
||||
|
||||
final int frames = getAnimationFrames();
|
||||
|
||||
item.addPropertyOverride(new ResourceLocation("conjure"), new IItemPropertyGetter(){
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float apply(ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity){
|
||||
return stack.getItemDamage() < frames ? (float)stack.getItemDamage() / frames
|
||||
: (float)(stack.getMaxDamage() - stack.getItemDamage()) / frames;
|
||||
}
|
||||
});
|
||||
item.addPropertyOverride(new ResourceLocation("conjuring"), new IItemPropertyGetter(){
|
||||
@SideOnly(Side.CLIENT)
|
||||
public float apply(ItemStack stack, @Nullable World world, @Nullable EntityLivingBase entity){
|
||||
return stack.getItemDamage() < frames
|
||||
|| stack.getItemDamage() > stack.getMaxDamage() - frames ? 1.0F : 0.0F;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** Returns the number of frames in the conjuring/vanishing animation. Override to change the number of frames
|
||||
* set by {@link IConjuredItem#addAnimationPropertyOverrides()}. */
|
||||
default int getAnimationFrames(){
|
||||
return 8;
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingDropsEvent(LivingDropsEvent event){
|
||||
static void onLivingDropsEvent(LivingDropsEvent event){
|
||||
// Destroys conjured items if their caster dies.
|
||||
for(EntityItem item : event.getDrops()){
|
||||
if(item.getItem().getItem() instanceof IConjuredItem){
|
||||
// Apparently some mods don't behave and shove null items in the list, quite why I have no idea
|
||||
if(item != null && item.getItem() != null && item.getItem().getItem() instanceof IConjuredItem){
|
||||
item.setDead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onItemTossEvent(ItemTossEvent event){
|
||||
static void onItemTossEvent(ItemTossEvent event){
|
||||
// Prevents conjured items being thrown by dragging and dropping outside the inventory.
|
||||
if(event.getEntityItem().getItem().getItem() instanceof IConjuredItem){
|
||||
event.setCanceled(true);
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
/**
|
||||
* Interface for any items that store mana. This interface simply specifies methods for setting and getting the amount
|
||||
* of mana held in the item (plus a few convenience methods); implementations may differ between items.
|
||||
* <p></p>
|
||||
* In wizardry itself, mana is still implemented as durability, however, as of wizardry 4.2, the vanilla method
|
||||
* {@code Item.setDamage()} has been overridden to do nothing. Instead, mana must be interacted with using the methods
|
||||
* in this interface. This means operating via the item rather than the stack.
|
||||
* <p></p>
|
||||
* This change prevents general item repair methods working on mana items (see issues #66 and #153), and also allows
|
||||
* other items to implement mana differently if they wish. For example, a weapon that can cast spells as an ability
|
||||
* might want regular durability in addition to mana, so the mana might be stored in NBT instead. Items that do not use
|
||||
* durability to represent mana may need to do zero-checking themselves, as appropriate.
|
||||
* <p></p>
|
||||
* <i>Wizardry's items implement mana as the <b>inverse</b> of item damage; i.e. the <b>more damaged</b> the item, the
|
||||
* <b>less mana</b> it has. Beware of this when converting to the new system.</i>
|
||||
* <p></p>
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2
|
||||
*/
|
||||
public interface IManaStoringItem {
|
||||
|
||||
/** Returns the amount of mana contained in the given item stack. */
|
||||
int getMana(ItemStack stack);
|
||||
|
||||
/** Sets the amount of mana contained in the given item stack to the given value. This method does not perform any
|
||||
* checks for creative mode, etc. */
|
||||
void setMana(ItemStack stack, int mana);
|
||||
|
||||
/** Returns the maximum amount of mana that the given item stack can hold. */
|
||||
int getManaCapacity(ItemStack stack);
|
||||
|
||||
/**
|
||||
* Returns whether this item's mana should be displayed in the arcane workbench tooltip. Only called client-side.
|
||||
* Ignore this method if this item is not an {@link IWorkbenchItem}.
|
||||
* @param player The player using the workbench.
|
||||
* @param stack The itemstack to query.
|
||||
* @return True if the mana should be shown, false if not. Returns true by default.
|
||||
*/
|
||||
default boolean showManaInWorkbench(EntityPlayer player, ItemStack stack){
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Convenience method that decreases the amount of mana contained in the given item stack by the given value. This
|
||||
* method automatically limits the mana to a minimum of 0 and performs the relevant checks for creative mode, etc. */
|
||||
default void consumeMana(ItemStack stack, int mana, EntityLivingBase wielder){
|
||||
if(wielder instanceof EntityPlayer && ((EntityPlayer)wielder).isCreative()) return; // Mana isn't consumed in creative
|
||||
setMana(stack, Math.max(getMana(stack) - mana, 0));
|
||||
}
|
||||
|
||||
/** Convenience method that increases the amount of mana contained in the given item stack by the given value.
|
||||
* This method automatically limits the mana to within the item's capacity. */
|
||||
// We don't really need to limit this one because Item#setDamage() ultimately limits it anyway, but we may as well
|
||||
default void rechargeMana(ItemStack stack, int mana){
|
||||
setMana(stack, Math.min(getMana(stack) + mana, getManaCapacity(stack)));
|
||||
}
|
||||
|
||||
/** Convenience method that returns true if the given stack contains the maximum amount of mana, false otherwise. */
|
||||
default boolean isManaFull(ItemStack stack){
|
||||
return getMana(stack) == getManaCapacity(stack);
|
||||
}
|
||||
|
||||
/** Convenience method that returns true if the given stack contains no mana, false otherwise. */
|
||||
default boolean isManaEmpty(ItemStack stack){
|
||||
return getMana(stack) == 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Interface for items that change their texture depending on their metadata. This is mainly to facilitate use of the
|
||||
* convenience method {@link electroblob.wizardry.client.model.WizardryModels#registerMultiTexturedModel(Item) WizardryModels.registerMultiTexturedModel(T)}. Also works well for {@code ItemBlock}s!
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2
|
||||
* @see ItemBlockMultiTexturedElemental
|
||||
*/
|
||||
public interface IMultiTexturedItem {
|
||||
|
||||
/**
|
||||
* Returns the appropriate {@code ResourceLocation} for this item's model, based on the given itemstack.
|
||||
* @param stack The itemstack to return the model name for.
|
||||
* @return A {@code ResourceLocation} pointing to the appropriate model file. As with any other model, this should
|
||||
* include the domain (mod ID) and filename, without the rest of the filepath.
|
||||
*/
|
||||
ResourceLocation getModelName(ItemStack stack);
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Interface for items that can hold and cast one or more spells. These may be consumables, like scrolls, or they may be
|
||||
* durability-based, like wands. Custom spell casting items should implement this interface to integrate properly into
|
||||
* wizardry. <i>It is no longer necessary to extend {@code ItemWand}, but you may still do so instead of implementing
|
||||
* this interface if appropriate.</i>
|
||||
* <p></p>
|
||||
* This interface is used for the following:<br>
|
||||
* - General-purpose detection of continuous spell casting (see {@link electroblob.wizardry.util.WizardryUtilities#isCasting(EntityLivingBase, Spell)})<br>
|
||||
* - Display of the arcane workbench tooltip (in conjunction with {@link IManaStoringItem})<br>
|
||||
* - Spell HUD visibility<br>
|
||||
* - Spell switching controls (they won't do anything unless the player is holding an {@code ISpellCastingItem})<br>
|
||||
* - Artefacts that trigger a player's wands/scrolls to cast spells
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2
|
||||
*/
|
||||
// This could probably be turned into a capability at some point, but for the moment it's fine like this
|
||||
// As we've already noted, capabilities are only useful for optional dependencies anyway
|
||||
public interface ISpellCastingItem {
|
||||
|
||||
/**
|
||||
* Returns the spell currently equipped on the given itemstack. The given itemstack will be of this item.
|
||||
* @param stack The itemstack to query.
|
||||
* @return The currently equipped spell, or {@link electroblob.wizardry.registry.Spells#none Spells.none} if no spell
|
||||
* is equipped.
|
||||
*/
|
||||
@Nonnull
|
||||
Spell getCurrentSpell(ItemStack stack);
|
||||
|
||||
/**
|
||||
* Returns all the spells currently bound to the given itemstack. The given itemstack will be of this item.
|
||||
* @param stack The itemstack to query.
|
||||
* @return The bound spells, or {@link electroblob.wizardry.registry.Spells#none Spells.none} if no spell
|
||||
* is equipped.
|
||||
*/
|
||||
default Spell[] getSpells(ItemStack stack){
|
||||
return new Spell[]{getCurrentSpell(stack)}; // Default implementation for single-spell items, because I'm lazy
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the next spell bound to the given itemstack. The given itemstack will be of this item.
|
||||
* @param stack The itemstack to query.
|
||||
*/
|
||||
default void selectNextSpell(ItemStack stack){
|
||||
// If it doesn't need spell-switching then don't bother the implementor with it
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the previous spell bound to the given itemstack. The given itemstack will be of this item.
|
||||
* @param stack The itemstack to query.
|
||||
*/
|
||||
default void selectPreviousSpell(ItemStack stack){
|
||||
// Nothing here either
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the spell HUD should be shown when a player is holding this item. Only called client-side.
|
||||
* @param player The player holding the item.
|
||||
* @param stack The itemstack to query.
|
||||
* @return True if the spell HUD should be shown, false if not.
|
||||
*/
|
||||
boolean showSpellHUD(EntityPlayer player, ItemStack stack);
|
||||
|
||||
/**
|
||||
* Returns whether this item's spells should be displayed in the arcane workbench tooltip. Only called client-side.
|
||||
* Ignore this method if this item is not an {@link IWorkbenchItem}.
|
||||
* @param player The player using the workbench.
|
||||
* @param stack The itemstack to query.
|
||||
* @return True if the spells should be shown, false if not. Returns true by default.
|
||||
*/
|
||||
default boolean showSpellsInWorkbench(EntityPlayer player, ItemStack stack){
|
||||
return true;
|
||||
}
|
||||
|
||||
// These methods were made with intention of standardising the code for casting spells using items.
|
||||
// For most external uses there's no reason for them to be separate, however, it makes more sense to do so because
|
||||
// then we can eliminate a bit of duplicate code from continuous vs. non-continuous spell casting. Otherwise, we'd
|
||||
// need a separate method for casting continuous spells anyway.
|
||||
|
||||
/**
|
||||
* Returns whether the given spell can be cast by the given stack in its current state. Does not perform any actual
|
||||
* spellcasting.
|
||||
*
|
||||
* @param stack The stack being queried; will be of this item.
|
||||
* @param spell The spell to be cast.
|
||||
* @param caster The player doing the casting.
|
||||
* @param hand The hand in which the casting item is being held.
|
||||
* @param castingTick For continuous spells, the number of ticks the spell has already been cast for. For all other
|
||||
* spells, this will be zero.
|
||||
* @param modifiers The modifiers with which the spell is being cast.
|
||||
* @return True if the spell can be cast, false if not.
|
||||
*/
|
||||
boolean canCast(ItemStack stack, Spell spell, EntityPlayer caster, EnumHand hand, int castingTick, SpellModifiers modifiers);
|
||||
|
||||
/**
|
||||
* Casts the given spell using the given item stack. <b>This method does not perform any checks</b>; these are done
|
||||
* in {@link ISpellCastingItem#canCast(ItemStack, Spell, EntityPlayer, EnumHand, int, SpellModifiers)}. This method
|
||||
* also performs any post-casting logic, such as mana costs and cooldowns.
|
||||
* <p></p>
|
||||
* <i>N.B. Continuous spell casting from outside of the items requires a bit of extra legwork, see
|
||||
* {@link WizardData} for an example.</i>
|
||||
*
|
||||
* @param stack The stack being queried; will be of this item.
|
||||
* @param spell The spell to be cast.
|
||||
* @param caster The player doing the casting.
|
||||
* @param hand The hand in which the casting item is being held.
|
||||
* @param castingTick For continuous spells, the number of ticks the spell has already been cast for. For all other
|
||||
* spells, this will be zero.
|
||||
* @param modifiers The modifiers with which the spell is being cast.
|
||||
* @return True if the spell succeeded, false if not. This is only really for the purpose of returning a result from
|
||||
* {@link net.minecraft.item.Item#onItemRightClick(World, EntityPlayer, EnumHand)} and similar methods; mana costs,
|
||||
* cooldowns and whatever else you might want to do post-spellcasting should be done within this method so that
|
||||
* external sources don't allow spells to be cast for free, for example.
|
||||
*/
|
||||
boolean cast(ItemStack stack, Spell spell, EntityPlayer caster, EnumHand hand, int castingTick, SpellModifiers modifiers);
|
||||
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import net.minecraft.item.ItemStack;
|
||||
* Items that implement this interface may be placed in the central slot of the arcane workbench as long as
|
||||
* {@link IWorkbenchItem#canPlace(ItemStack)} returns true. The number of spell book slots displayed is also specified
|
||||
* using {@link IWorkbenchItem#getSpellSlotCount(ItemStack)}.
|
||||
* <p>
|
||||
* <p></p>
|
||||
* Items that implement this interface define what happens if they are in the central slot of the arcane workbench and
|
||||
* the apply button is pressed, in {@link IWorkbenchItem#onApplyButtonPressed(EntityPlayer, Slot, Slot, Slot, Slot[])}.
|
||||
* This is a core part of the arcane workbench refactoring in version 4.2 and allows for custom spell casting items and
|
||||
@@ -52,5 +52,13 @@ public interface IWorkbenchItem {
|
||||
* @return True if anything changed, false if not.
|
||||
*/
|
||||
boolean onApplyButtonPressed(EntityPlayer player, Slot centre, Slot crystals, Slot upgrade, Slot[] spellBooks);
|
||||
|
||||
/**
|
||||
* Returns whether the tooltip (dark grey box) should be drawn when this item is in an arcane workbench. Only
|
||||
* called client-side.
|
||||
* @param stack The itemstack to query.
|
||||
* @return True if the workbench tooltip should be shown, false if not.
|
||||
*/
|
||||
boolean showTooltip(ItemStack stack);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import net.minecraft.client.util.ITooltipFlag;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.item.EnumRarity;
|
||||
import net.minecraft.item.Item;
|
||||
@@ -15,6 +12,8 @@ import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ItemArcaneTome extends Item {
|
||||
|
||||
public ItemArcaneTome(){
|
||||
@@ -54,7 +53,12 @@ public class ItemArcaneTome extends Item {
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void addInformation(ItemStack stack, World world, List<String> tooltip, ITooltipFlag showAdvanced){
|
||||
public void addInformation(ItemStack stack, World world, List<String> tooltip, net.minecraft.client.util.ITooltipFlag showAdvanced){
|
||||
|
||||
if(stack.getItemDamage() < 1){
|
||||
return; // If something's up with the metadata it will display a 'generic' tome of arcana with no info
|
||||
}
|
||||
|
||||
Tier tier = Tier.values()[stack.getItemDamage()];
|
||||
Tier tier2 = Tier.values()[stack.getItemDamage() - 1];
|
||||
tooltip.add(tier.getDisplayNameWithFormatting());
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import net.minecraft.client.util.ITooltipFlag;
|
||||
import net.minecraft.item.EnumRarity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -14,6 +9,9 @@ import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public class ItemArmourUpgrade extends Item {
|
||||
|
||||
public ItemArmourUpgrade(){
|
||||
@@ -35,7 +33,7 @@ public class ItemArmourUpgrade extends Item {
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void addInformation(ItemStack stack, @Nullable World worldIn, List<String> tooltip, ITooltipFlag flagIn) {
|
||||
public void addInformation(ItemStack stack, @Nullable World worldIn, List<String> tooltip, net.minecraft.client.util.ITooltipFlag flagIn) {
|
||||
tooltip.add(net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":armour_upgrade.desc1", "\u00A77"));
|
||||
tooltip.add(
|
||||
net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":armour_upgrade.desc2", "\u00A77", "\u00A7d"));
|
||||
|
||||
@@ -0,0 +1,875 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.entity.construct.EntityFireRing;
|
||||
import electroblob.wizardry.entity.living.ISummonedCreature;
|
||||
import electroblob.wizardry.entity.projectile.EntityDart;
|
||||
import electroblob.wizardry.entity.projectile.EntityForceOrb;
|
||||
import electroblob.wizardry.entity.projectile.EntityIceShard;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.integration.DamageSafetyChecker;
|
||||
import electroblob.wizardry.integration.baubles.WizardryBaublesIntegration;
|
||||
import electroblob.wizardry.registry.*;
|
||||
import electroblob.wizardry.spell.*;
|
||||
import electroblob.wizardry.util.*;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.IProjectile;
|
||||
import net.minecraft.entity.item.EntityItem;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.MobEffects;
|
||||
import net.minecraft.item.EnumRarity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.crafting.FurnaceRecipes;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.Biome;
|
||||
import net.minecraftforge.common.BiomeDictionary;
|
||||
import net.minecraftforge.common.capabilities.ICapabilityProvider;
|
||||
import net.minecraftforge.event.entity.living.LivingDeathEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingHurtEvent;
|
||||
import net.minecraftforge.event.entity.living.PotionEvent;
|
||||
import net.minecraftforge.event.entity.player.PlayerDropsEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.Event;
|
||||
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 net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Base class for all artefact items, which handles effects, textures and so on. The majority of artefacts are
|
||||
* event-driven so it is unlikely that this class will need to be extended, unless other {@code Item} methods are to be
|
||||
* overridden.
|
||||
* <p></p>
|
||||
* This class contains methods and an enum that mirror those in {@code IBauble} from the Baubles mod. If Baubles is
|
||||
* loaded, these are called via the bauble capability; otherwise, they are called from regular {@link Item} methods
|
||||
* or events with appropriate checks. This allows wizardry to run with Baubles as an optional dependency.
|
||||
* <p></p>
|
||||
* <b>Do not reference any Baubles classes from subclasses of this</b>, or the dependency will no longer be optional!
|
||||
* Use {@link ItemArtefact#isArtefactActive(EntityPlayer, Item)} to test if a particular artefact is active. Use
|
||||
* {@link ItemArtefact#getActiveArtefacts(EntityPlayer, Type...)} to get a list of active artefacts.
|
||||
* <p></p>
|
||||
* @author Electroblob
|
||||
* @since Wizardry 4.2
|
||||
* @see electroblob.wizardry.integration.baubles.WizardryBaublesIntegration
|
||||
*/
|
||||
@Mod.EventBusSubscriber
|
||||
public class ItemArtefact extends Item {
|
||||
|
||||
// Artefact checklist:
|
||||
// - Create and register item, add model and texture
|
||||
// - Program effect, using events if possible (if it only affects a specific spell or entity, in there is ok)
|
||||
// - Add name AND description to lang files
|
||||
// - Add to loot_tables/subsets/[rarity]_artefacts.json
|
||||
// - Add to advancements/artefact.json and advancements/all_artefacts.json
|
||||
|
||||
public enum Type {
|
||||
|
||||
/** An artefact that improves attacking spells. Two of these can be active at any one time. */ RING(2),
|
||||
/** An artefact that improves defensive spells. One of these can be active at any one time. */ AMULET(1),
|
||||
/** An artefact that improves utility spells. One of these can be active at any one time. */ CHARM(1);
|
||||
|
||||
public final int maxAtOnce;
|
||||
|
||||
Type(int maxAtOnce){
|
||||
this.maxAtOnce = maxAtOnce;
|
||||
}
|
||||
}
|
||||
|
||||
// If Baubles is not installed, artefacts will still work, but must instead be
|
||||
// on the player's hotbar (and only the first n of a given type will work, where n is the number of baubles slots of that
|
||||
// artefact's type).
|
||||
|
||||
// Rarity was chosen over Tier here for a couple of reasons: firstly, displaying a tier would add unnecessary clutter
|
||||
// to a potentially already-long tooltip whereas rarity provides a compact, neat way of displaying it that everyone is
|
||||
// reasonably familiar with. Secondly, rarity makes more sense for an artefact, since it's something you find rather
|
||||
// than upgrade to, and artefacts are not tied to tiers of wand/spell/whatever - they can be used whenever.
|
||||
private final EnumRarity rarity;
|
||||
private final Type type;
|
||||
|
||||
public ItemArtefact(EnumRarity rarity, Type type){
|
||||
setMaxStackSize(1);
|
||||
setCreativeTab(WizardryTabs.GEAR);
|
||||
this.rarity = rarity;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumRarity getRarity(ItemStack stack){
|
||||
return rarity;
|
||||
}
|
||||
|
||||
public Type getType(){
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEffect(ItemStack stack){
|
||||
return rarity == EnumRarity.EPIC;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void addInformation(ItemStack stack, @Nullable World worldIn, List<String> tooltip, net.minecraft.client.util.ITooltipFlag flagIn){
|
||||
Wizardry.proxy.addMultiLineDescription(tooltip, "item." + this.getRegistryName() + ".desc");
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable NBTTagCompound nbt){
|
||||
return WizardryBaublesIntegration.enabled() ? new WizardryBaublesIntegration.ArtefactBaubleProvider(type) : null;
|
||||
}
|
||||
|
||||
// IBauble does of course have an onWornTick method. However, because it's an optional dependency, it doesn't really
|
||||
// make sense to use that method when it's easier to just use the isBaubleEquipped method in the same
|
||||
// place as the non-baubles check. In other words, most artefacts are event-driven anyway so I'd rather have the
|
||||
// tick-driven ones use events as well for the sake of consistency.
|
||||
|
||||
/**
|
||||
* Returns whether the given artefact is active for the given player. If Baubles is loaded, an artefact is active
|
||||
* when it is equipped in an appropriate bauble slot. If Baubles is not loaded, an artefact is active if it is one
|
||||
* of the first n of its type on the player's hands/hotbar, where n is the number of bauble slots of that type.
|
||||
* <p></p>
|
||||
* N.B. This method is inefficient if you are defining multiple artefact behaviours in the same place. In this use
|
||||
* case, it is preferable to use {@link ItemArtefact#getActiveArtefacts(EntityPlayer, Type...)}.
|
||||
*
|
||||
* @param player The player whose inventory is to be checked.
|
||||
* @param artefact The artefact to check for.
|
||||
* @return True if the player has the artefact and it is active, false if not. Always returns false if the given
|
||||
* item is not an instance of {@code ItemArtefact}.
|
||||
* @throws IllegalArgumentException If the given item is not an artefact.
|
||||
*/
|
||||
// It's cleaner to cast to ItemArtefact here than wherever it is used - items can't be stored as ItemWhatever objects
|
||||
public static boolean isArtefactActive(EntityPlayer player, Item artefact){
|
||||
|
||||
if(!(artefact instanceof ItemArtefact)) throw new IllegalArgumentException("Not an artefact!");
|
||||
|
||||
if(WizardryBaublesIntegration.enabled()){
|
||||
return WizardryBaublesIntegration.isBaubleEquipped(player, artefact);
|
||||
}else{
|
||||
// To find out if the given artefact is one of the first n on the player's hotbar (where n is the maximum
|
||||
// number of that kind of artefact that can be active at once):
|
||||
return WizardryUtilities.getPrioritisedHotbarAndOffhand(player).stream() // Retrieve the stacks in question
|
||||
// Filter out all except artefacts of the same type as the given one (preserving order)
|
||||
.filter(s -> s.getItem() instanceof ItemArtefact && ((ItemArtefact)s.getItem()).type == ((ItemArtefact)artefact).type)
|
||||
.limit(((ItemArtefact)artefact).type.maxAtOnce) // Ignore all but the first n
|
||||
.anyMatch(s -> s.getItem() == artefact); // Check if the remaining stacks contain the artefact
|
||||
// Note that streaming a list DOES retain the order (unless you call unordered(), obviously)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the currently active artefacts for the given player. If Baubles is loaded, an artefact is active
|
||||
* when it is equipped in an appropriate bauble slot. If Baubles is not loaded, an artefact is active if it is one
|
||||
* of the first n of its type on the player's hands/hotbar, where n is the number of bauble slots of that type.
|
||||
* <p></p>
|
||||
* This method is more efficient for processing multiple artefact behaviours at once.
|
||||
*
|
||||
* @param player The player whose inventory is to be checked.
|
||||
* @param types The artefact types to check for. If omitted, all artefact types will be checked.
|
||||
* @return True if the player has the artefact and it is active, false if not. Always returns false if the given
|
||||
* item is not an instance of {@code ItemArtefact}.
|
||||
*/
|
||||
public static List<ItemArtefact> getActiveArtefacts(EntityPlayer player, Type... types){
|
||||
|
||||
if(types.length == 0) types = Type.values();
|
||||
|
||||
if(WizardryBaublesIntegration.enabled()){
|
||||
return WizardryBaublesIntegration.getEquippedArtefacts(player, types);
|
||||
}else{
|
||||
|
||||
List<ItemArtefact> artefacts = new ArrayList<>();
|
||||
|
||||
for(Type type : types){
|
||||
artefacts.addAll(WizardryUtilities.getPrioritisedHotbarAndOffhand(player).stream()
|
||||
.filter(s -> s.getItem() instanceof ItemArtefact)
|
||||
.map(s -> (ItemArtefact)s.getItem())
|
||||
.filter(i -> type == i.type)
|
||||
.limit(type.maxAtOnce)
|
||||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
return artefacts;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that scans through all wands on the given player's hotbar and offhand and executes the given action
|
||||
* if any of them have the given spell bound to them. This is a useful code pattern for artefact effects.
|
||||
*
|
||||
* @param player The player whose hotbar is to be checked
|
||||
* @param spell The spell to search for
|
||||
* @param action A {@link Consumer} specifying the action to be performed if a wand with the given spell is found.
|
||||
* The stack passed to this consumer will be the wand in question.
|
||||
* @return True if the action was executed, false otherwise.
|
||||
*/
|
||||
public static boolean findMatchingWandAndExecute(EntityPlayer player, Spell spell, Consumer<? super ItemStack> action){
|
||||
|
||||
List<ItemStack> hotbar = WizardryUtilities.getPrioritisedHotbarAndOffhand(player);
|
||||
|
||||
Optional<ItemStack> stack = hotbar.stream().filter(s -> s.getItem() instanceof ISpellCastingItem
|
||||
&& Arrays.asList(((ISpellCastingItem)s.getItem()).getSpells(s)).contains(spell)).findFirst();
|
||||
|
||||
stack.ifPresent(action);
|
||||
return stack.isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that scans through all wands on the given player's hotbar and offhand and casts the given spell if
|
||||
* it is bound to any of them. This is a useful code pattern for artefact effects.
|
||||
*
|
||||
* @param player The player whose hotbar is to be checked
|
||||
* @param spell The spell to search for and cast
|
||||
* @return True if the spell was cast, false otherwise.
|
||||
*/
|
||||
public static boolean findMatchingWandAndCast(EntityPlayer player, Spell spell){
|
||||
|
||||
return findMatchingWandAndExecute(player, spell, wand -> {
|
||||
|
||||
SpellModifiers modifiers = new SpellModifiers();
|
||||
|
||||
if(((ISpellCastingItem)wand.getItem()).canCast(wand, spell, player, EnumHand.MAIN_HAND, 0, modifiers)){
|
||||
((ISpellCastingItem)wand.getItem()).cast(wand, spell, player, EnumHand.MAIN_HAND, 0, modifiers);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ================================================ Event Handlers ================================================
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPlayerTickEvent(TickEvent.PlayerTickEvent event){
|
||||
|
||||
if(event.phase == TickEvent.Phase.START){
|
||||
|
||||
EntityPlayer player = event.player;
|
||||
World world = player.world;
|
||||
|
||||
for(ItemArtefact artefact : getActiveArtefacts(player)){
|
||||
|
||||
if(artefact == WizardryItems.ring_condensing){
|
||||
|
||||
if(world.isRemote && player.ticksExisted % 150 == 0){
|
||||
for(ItemStack stack : WizardryUtilities.getHotbar(player)){
|
||||
// Needs to be both of these interfaces because this ring only recharges wands
|
||||
// (or more accurately, chargeable spellcasting items)
|
||||
if(stack.getItem() instanceof ISpellCastingItem && stack.getItem() instanceof IManaStoringItem)
|
||||
((IManaStoringItem)stack.getItem()).rechargeMana(stack, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.amulet_arcane_defence){
|
||||
|
||||
if(world.isRemote && player.ticksExisted % 300 == 0){
|
||||
for(ItemStack stack : player.getArmorInventoryList()){
|
||||
// IManaStoringItem is sufficient, since anything in the armour slots is probably armour
|
||||
if(stack.getItem() instanceof IManaStoringItem)
|
||||
((IManaStoringItem)stack.getItem()).rechargeMana(stack, 1);
|
||||
}
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.amulet_recovery){
|
||||
|
||||
if(player.shouldHeal() && player.getHealth() < player.getMaxHealth()/2
|
||||
&& player.ticksExisted % 50 == 0){
|
||||
|
||||
int totalArmourMana = Streams.stream(player.getArmorInventoryList())
|
||||
.filter(s -> s.getItem() instanceof IManaStoringItem)
|
||||
.mapToInt(s -> ((IManaStoringItem)s.getItem()).getMana(s))
|
||||
.sum();
|
||||
|
||||
if(totalArmourMana >= 2){
|
||||
player.heal(1);
|
||||
// 2 mana per half-heart, randomly distributed
|
||||
List<ItemStack> chargedArmour = Streams.stream(player.getArmorInventoryList())
|
||||
.filter(s -> s.getItem() instanceof IManaStoringItem)
|
||||
.filter(s -> !((IManaStoringItem)s.getItem()).isManaEmpty(s))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if(chargedArmour.size() == 1){
|
||||
((IManaStoringItem)chargedArmour.get(0).getItem()).consumeMana(chargedArmour.get(0), 2, player);
|
||||
}else{
|
||||
Collections.shuffle(chargedArmour);
|
||||
((IManaStoringItem)chargedArmour.get(0).getItem()).consumeMana(chargedArmour.get(0), 1, player);
|
||||
((IManaStoringItem)chargedArmour.get(1).getItem()).consumeMana(chargedArmour.get(1), 1, player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.amulet_glide){
|
||||
// This should be a chance per fall, so we can't just check fall distance is greater than 3 each tick
|
||||
// Based on a stationary start and a gravity acceleration of 0.02 blocks/tick^2, at 3 blocks of fall
|
||||
// distance the player should be falling at about 0.35b/t, so 0.5 blocks should be enough of a window
|
||||
if(player.fallDistance > 3f && player.fallDistance < 3.5f && player.world.rand.nextFloat() < 0.5f){
|
||||
if(!WizardData.get(player).isCasting()) WizardData.get(player).startCastingContinuousSpell(Spells.glide, new SpellModifiers(), 600);
|
||||
}else if(player.onGround){
|
||||
WizardData data = WizardData.get(player);
|
||||
if(data.currentlyCasting() == Spells.glide) data.stopCastingContinuousSpell();
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.amulet_auto_shield){
|
||||
|
||||
findMatchingWandAndExecute(player, Spells.shield, wand -> {
|
||||
|
||||
List<Entity> projectiles = WizardryUtilities.getEntitiesWithinRadius(5, player.posX, player.posY, player.posZ, world, Entity.class);
|
||||
projectiles.removeIf(e -> !(e instanceof IProjectile));
|
||||
Vec3d look = player.getLookVec();
|
||||
Vec3d playerPos = player.getPositionVector().add(0, player.height/2, 0);
|
||||
|
||||
for(Entity projectile : projectiles){
|
||||
Vec3d vec = playerPos.subtract(projectile.getPositionVector()).normalize();
|
||||
double angle = Math.acos(vec.scale(-1).dotProduct(look));
|
||||
if(angle > Math.PI * 0.4f) continue; // (Roughly) the angle the shield will protect
|
||||
Vec3d velocity = new Vec3d(projectile.motionX, projectile.motionY, projectile.motionZ).normalize();
|
||||
double angle1 = Math.acos(vec.dotProduct(velocity));
|
||||
if(angle1 < Math.PI * 0.2f){
|
||||
SpellModifiers modifiers = new SpellModifiers();
|
||||
if(((ISpellCastingItem)wand.getItem()).canCast(wand, Spells.shield, player, EnumHand.MAIN_HAND, 0, modifiers)){
|
||||
((ISpellCastingItem)wand.getItem()).cast(wand, Spells.shield, player, EnumHand.MAIN_HAND, 0, modifiers);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}else if(artefact == WizardryItems.charm_feeding){
|
||||
// Every 5 seconds, feed the player if they are near starving
|
||||
if(player.ticksExisted % 100 == 0 && player.getFoodStats().getFoodLevel() < 2){
|
||||
findMatchingWandAndCast(player, Spells.replenish_hunger);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOW)
|
||||
public static void onSpellCastPreEvent(SpellCastEvent.Pre event){
|
||||
|
||||
if(event.getCaster() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getCaster();
|
||||
SpellModifiers modifiers = event.getModifiers();
|
||||
|
||||
for(ItemArtefact artefact : getActiveArtefacts(player)){
|
||||
|
||||
float potency = modifiers.get(SpellModifiers.POTENCY);
|
||||
float cooldown = modifiers.get(WizardryItems.cooldown_upgrade);
|
||||
Biome biome = player.world.getBiome(player.getPosition());
|
||||
|
||||
if(artefact == WizardryItems.ring_battlemage){
|
||||
|
||||
if(player.getHeldItemOffhand().getItem() instanceof ISpellCastingItem
|
||||
&& ImbueWeapon.isSword(player.getHeldItemMainhand().getItem())){
|
||||
modifiers.set(SpellModifiers.POTENCY, 1.1f * potency, false);
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_fire_biome){
|
||||
|
||||
if(event.getSpell().getElement() == Element.FIRE
|
||||
&& BiomeDictionary.hasType(biome, BiomeDictionary.Type.HOT)
|
||||
&& BiomeDictionary.hasType(biome, BiomeDictionary.Type.DRY)){
|
||||
modifiers.set(SpellModifiers.POTENCY, 1.3f * potency, false);
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_ice_biome){
|
||||
|
||||
if(event.getSpell().getElement() == Element.ICE
|
||||
&& BiomeDictionary.hasType(biome, BiomeDictionary.Type.SNOWY)){
|
||||
modifiers.set(SpellModifiers.POTENCY, 1.3f * potency, false);
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_earth_biome){
|
||||
|
||||
if(event.getSpell().getElement() == Element.EARTH
|
||||
// If it was any forest that would be far too many, so taigas and jungles are excluded
|
||||
&& BiomeDictionary.hasType(biome, BiomeDictionary.Type.FOREST)
|
||||
&& !BiomeDictionary.hasType(biome, BiomeDictionary.Type.CONIFEROUS)
|
||||
&& !BiomeDictionary.hasType(biome, BiomeDictionary.Type.JUNGLE)){
|
||||
modifiers.set(SpellModifiers.POTENCY, 1.3f * potency, false);
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_storm){
|
||||
|
||||
if(event.getSpell().getElement() == Element.LIGHTNING && player.world.isThundering()){
|
||||
modifiers.set(WizardryItems.cooldown_upgrade, cooldown * 0.3f, false);
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_full_moon){
|
||||
|
||||
if(event.getSpell().getElement() == Element.EARTH && !player.world.isDaytime()
|
||||
&& player.world.provider.getMoonPhase(player.world.getWorldTime()) == 0){
|
||||
modifiers.set(WizardryItems.cooldown_upgrade, cooldown * 0.3f, false);
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_blockwrangler){
|
||||
|
||||
if(event.getSpell() == Spells.greater_telekinesis){
|
||||
modifiers.set(SpellModifiers.POTENCY, modifiers.get(SpellModifiers.POTENCY) * 2, false);
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_conjurer){
|
||||
|
||||
if(event.getSpell() instanceof SpellConjuration){
|
||||
modifiers.set(WizardryItems.duration_upgrade, modifiers.get(WizardryItems.duration_upgrade) * 2, false);
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.charm_minion_health){
|
||||
// We COULD check the spell is a SpellMinion here, but there's really no point
|
||||
modifiers.set(SpellMinion.HEALTH_MODIFIER, 1.25f * modifiers.get(SpellMinion.HEALTH_MODIFIER), true);
|
||||
|
||||
}else if(artefact == WizardryItems.charm_flight){
|
||||
|
||||
if(event.getSpell() == Spells.flight || event.getSpell() == Spells.glide){
|
||||
// FIXME: Does not appear to be working, for some reason
|
||||
modifiers.set(SpellModifiers.POTENCY, 1.5f * potency, true);
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.charm_experience_tome){
|
||||
|
||||
modifiers.set(SpellModifiers.PROGRESSION, modifiers.get(SpellModifiers.PROGRESSION) * 1.5f, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onSpellCastPostEvent(SpellCastEvent.Pre event){
|
||||
|
||||
if(event.getCaster() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getCaster();
|
||||
|
||||
if(isArtefactActive(player, WizardryItems.ring_paladin)){
|
||||
|
||||
if(event.getSpell() instanceof Heal || event.getSpell() instanceof HealAlly || event.getSpell() instanceof GreaterHeal){
|
||||
// Spell properties allow all three of the above spells to be dealt with the same way - neat!
|
||||
float healthGained = event.getSpell().getProperty(Spell.HEALTH).floatValue() * event.getModifiers().get(SpellModifiers.POTENCY);
|
||||
|
||||
List<EntityLivingBase> nearby = WizardryUtilities.getEntitiesWithinRadius(4, player.posX, player.posY, player.posZ, event.getWorld());
|
||||
|
||||
for(EntityLivingBase entity : nearby){
|
||||
if(AllyDesignationSystem.isAllied(player, entity) && entity.getHealth() > 0 && entity.getHealth() < entity.getMaxHealth()){
|
||||
entity.heal(healthGained * 0.2f); // 1/5 of the amount healed by the spell itself
|
||||
if(event.getWorld().isRemote) ParticleBuilder.spawnHealParticles(event.getWorld(), entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingEvent.LivingUpdateEvent event){
|
||||
|
||||
EntityLivingBase entity = event.getEntityLiving();
|
||||
|
||||
// No point doing this every tick, every 2.5 seconds should be enough
|
||||
if(entity.ticksExisted % 50 == 0 && entity.isPotionActive(WizardryPotions.mind_control)){
|
||||
|
||||
NBTTagCompound entityNBT = entity.getEntityData();
|
||||
|
||||
if(entityNBT.hasUniqueId(MindControl.NBT_KEY)){
|
||||
|
||||
Entity caster = WizardryUtilities.getEntityByUUID(entity.world, entityNBT.getUniqueId(MindControl.NBT_KEY));
|
||||
|
||||
if(caster instanceof EntityPlayer){
|
||||
|
||||
if(isArtefactActive((EntityPlayer)caster, WizardryItems.ring_mind_control)){
|
||||
|
||||
WizardryUtilities.getEntitiesWithinRadius(3, entity.posX, entity.posY, entity.posZ, entity.world, EntityLiving.class).stream()
|
||||
.filter(e -> e.world.rand.nextInt(10) == 0)
|
||||
.filter(MindControl::canControl)
|
||||
.filter(e -> AllyDesignationSystem.isValidTarget(caster, e))
|
||||
.forEach(target -> MindControl.startControlling(target, (EntityPlayer)caster,
|
||||
// Control the new target for only the remaining duration, otherwise it could go on forever!
|
||||
entity.getActivePotionEffect(WizardryPotions.mind_control).getDuration()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingHurtEvent(LivingHurtEvent event){
|
||||
|
||||
if(event.getEntity() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getEntity();
|
||||
|
||||
for(ItemArtefact artefact : getActiveArtefacts(player)){
|
||||
|
||||
if(artefact == WizardryItems.amulet_warding){
|
||||
|
||||
if(!event.getSource().isUnblockable() && event.getSource().isMagicDamage()){
|
||||
event.setAmount(event.getAmount() * 0.9f);
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.amulet_fire_protection){
|
||||
|
||||
if(event.getSource().isFireDamage()) event.setAmount(event.getAmount() * 0.7f);
|
||||
|
||||
}else if(artefact == WizardryItems.amulet_ice_protection){
|
||||
|
||||
if(event.getSource() instanceof IElementalDamage
|
||||
&& ((IElementalDamage)event.getSource()).getType() == MagicDamage.DamageType.FROST)
|
||||
event.setAmount(event.getAmount() * 0.7f);
|
||||
|
||||
}else if(artefact == WizardryItems.amulet_channeling){
|
||||
|
||||
if(player.world.rand.nextFloat() < 0.3f && event.getSource() instanceof IElementalDamage
|
||||
&& ((IElementalDamage)event.getSource()).getType() == MagicDamage.DamageType.SHOCK){
|
||||
event.setCanceled(true);
|
||||
return;
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.amulet_fire_cloaking){
|
||||
|
||||
if(!event.getSource().isUnblockable()){
|
||||
|
||||
List<EntityFireRing> fireRings = player.world.getEntitiesWithinAABB(EntityFireRing.class, player.getEntityBoundingBox());
|
||||
|
||||
for(EntityFireRing fireRing : fireRings){
|
||||
if(fireRing.getCaster() instanceof EntityPlayer && (fireRing.getCaster() == player
|
||||
|| AllyDesignationSystem.isOwnerAlly(player, fireRing))){
|
||||
event.setAmount(event.getAmount() * 0.25f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.amulet_potential){
|
||||
|
||||
if(player.world.rand.nextFloat() < 0.2f && WizardryUtilities.isMeleeDamage(event.getSource())
|
||||
&& event.getSource().getTrueSource() instanceof EntityLivingBase){
|
||||
|
||||
EntityLivingBase target = (EntityLivingBase)event.getSource().getTrueSource();
|
||||
|
||||
if(player.world.isRemote){
|
||||
|
||||
ParticleBuilder.create(ParticleBuilder.Type.LIGHTNING).entity(event.getEntity())
|
||||
.pos(0, event.getEntity().height/2, 0).target(target).spawn(player.world);
|
||||
|
||||
ParticleBuilder.spawnShockParticles(player.world, target.posX,
|
||||
target.getEntityBoundingBox().minY + target.height/2, target.posZ);
|
||||
}
|
||||
|
||||
DamageSafetyChecker.attackEntitySafely(target, MagicDamage.causeDirectMagicDamage(player,
|
||||
MagicDamage.DamageType.SHOCK, true), Spells.static_aura.getProperty(Spell.DAMAGE).floatValue(), event.getSource().getDamageType());
|
||||
target.playSound(WizardrySounds.SPELL_STATIC_AURA_RETALIATE, 1.0F, player.world.rand.nextFloat() * 0.4F + 1.5F);
|
||||
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.amulet_lich){
|
||||
|
||||
if(!event.getSource().isUnblockable() && player.world.rand.nextFloat() < 0.15f){
|
||||
|
||||
List<EntityLiving> nearbyMobs = WizardryUtilities.getEntitiesWithinRadius(5, player.posX, player.posY, player.posZ, player.world, EntityLiving.class);
|
||||
nearbyMobs.removeIf(e -> !(e instanceof ISummonedCreature && ((ISummonedCreature)e).getCaster() == player));
|
||||
|
||||
if(!nearbyMobs.isEmpty()){
|
||||
Collections.shuffle(nearbyMobs);
|
||||
// Even though we're passing the same damage source through, we still need the safety check
|
||||
DamageSafetyChecker.attackEntitySafely(nearbyMobs.get(0), event.getSource(), event.getAmount(), event.getSource().getDamageType());
|
||||
event.setCanceled(true);
|
||||
return; // Standard practice: stop as soon as the event is canceled
|
||||
}
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.amulet_banishing){
|
||||
|
||||
if(player.world.rand.nextFloat() < 0.2f && WizardryUtilities.isMeleeDamage(event.getSource())
|
||||
&& event.getSource().getTrueSource() instanceof EntityLivingBase){
|
||||
|
||||
EntityLivingBase target = (EntityLivingBase)event.getSource().getTrueSource();
|
||||
((Banish)Spells.banish).teleport(target, target.world, 8 + target.world.rand.nextDouble() * 8);
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.amulet_transience){
|
||||
|
||||
if(player.getHealth() <= 2 && player.world.rand.nextFloat() < 0.25f){
|
||||
player.addPotionEffect(new PotionEffect(WizardryPotions.transience, 300));
|
||||
player.addPotionEffect(new PotionEffect(MobEffects.INVISIBILITY, 300, 0, false, false));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(event.getSource().getTrueSource() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getSource().getTrueSource();
|
||||
ItemStack mainhandItem = player.getHeldItemMainhand();
|
||||
World world = player.world;
|
||||
|
||||
for(ItemArtefact artefact : getActiveArtefacts(player)){
|
||||
|
||||
if(artefact == WizardryItems.ring_fire_melee){
|
||||
// Used ItemWand intentionally because we need the element
|
||||
// Other mods can always make their own events if they want their own spellcasting items to do this
|
||||
if(WizardryUtilities.isMeleeDamage(event.getSource()) && mainhandItem.getItem() instanceof ItemWand
|
||||
&& ((ItemWand)mainhandItem.getItem()).element == Element.FIRE){
|
||||
event.getEntity().setFire(5);
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_ice_melee){
|
||||
|
||||
if(WizardryUtilities.isMeleeDamage(event.getSource()) && mainhandItem.getItem() instanceof ItemWand
|
||||
&& ((ItemWand)mainhandItem.getItem()).element == Element.ICE){
|
||||
event.getEntityLiving().addPotionEffect(new PotionEffect(WizardryPotions.frost, 200, 0));
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_lightning_melee){
|
||||
|
||||
if(WizardryUtilities.isMeleeDamage(event.getSource()) && mainhandItem.getItem() instanceof ItemWand
|
||||
&& ((ItemWand)mainhandItem.getItem()).element == Element.LIGHTNING){
|
||||
|
||||
WizardryUtilities.getEntitiesWithinRadius(3, player.posX, player.posY, player.posZ, world).stream()
|
||||
.filter(WizardryUtilities::isLiving)
|
||||
.min(Comparator.comparingDouble(player::getDistanceSq))
|
||||
.ifPresent(target -> {
|
||||
|
||||
if(world.isRemote){
|
||||
|
||||
ParticleBuilder.create(ParticleBuilder.Type.LIGHTNING).entity(event.getEntity())
|
||||
.pos(0, event.getEntity().height/2, 0).target(target).spawn(world);
|
||||
|
||||
ParticleBuilder.spawnShockParticles(world, target.posX,
|
||||
target.getEntityBoundingBox().minY + target.height/2, target.posZ);
|
||||
}
|
||||
|
||||
DamageSafetyChecker.attackEntitySafely(target, MagicDamage.causeDirectMagicDamage(player,
|
||||
MagicDamage.DamageType.SHOCK, true), Spells.static_aura.getProperty(Spell.DAMAGE).floatValue(), event.getSource().getDamageType());
|
||||
target.playSound(WizardrySounds.SPELL_STATIC_AURA_RETALIATE, 1.0F, world.rand.nextFloat() * 0.4F + 1.5F);
|
||||
});
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_necromancy_melee){
|
||||
|
||||
if(WizardryUtilities.isMeleeDamage(event.getSource()) && mainhandItem.getItem() instanceof ItemWand
|
||||
&& ((ItemWand)mainhandItem.getItem()).element == Element.NECROMANCY){
|
||||
event.getEntityLiving().addPotionEffect(new PotionEffect(MobEffects.WITHER, 200, 0));
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_earth_melee){
|
||||
|
||||
if(WizardryUtilities.isMeleeDamage(event.getSource()) && mainhandItem.getItem() instanceof ItemWand
|
||||
&& ((ItemWand)mainhandItem.getItem()).element == Element.EARTH){
|
||||
event.getEntityLiving().addPotionEffect(new PotionEffect(MobEffects.POISON, 200, 0));
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_shattering){
|
||||
|
||||
if(!player.world.isRemote && player.world.rand.nextFloat() < 0.15f
|
||||
&& event.getEntityLiving().getHealth() < 12f // Otherwise it's a bit overpowered!
|
||||
&& event.getEntityLiving().isPotionActive(WizardryPotions.frost)
|
||||
&& WizardryUtilities.isMeleeDamage(event.getSource())){
|
||||
|
||||
event.setAmount(12f);
|
||||
|
||||
for(int i = 0; i < 8; i++){
|
||||
double dx = event.getEntity().world.rand.nextDouble() - 0.5;
|
||||
double dy = event.getEntity().world.rand.nextDouble() - 0.5;
|
||||
double dz = event.getEntity().world.rand.nextDouble() - 0.5;
|
||||
EntityIceShard iceshard = new EntityIceShard(event.getEntity().world);
|
||||
iceshard.setPosition(event.getEntity().posX + dx + Math.signum(dx) * event.getEntity().width,
|
||||
event.getEntity().posY + event.getEntity().height/2 + dy,
|
||||
event.getEntity().posZ + dz + Math.signum(dz) * event.getEntity().width);
|
||||
iceshard.motionX = dx * 1.5;
|
||||
iceshard.motionY = dy * 1.5;
|
||||
iceshard.motionZ = dz * 1.5;
|
||||
iceshard.setCaster(player);
|
||||
event.getEntity().world.spawnEntity(iceshard);
|
||||
}
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_soulbinding){
|
||||
|
||||
// Best guess at necromancy spell damage: either it's wither damage...
|
||||
if((event.getSource() instanceof IElementalDamage
|
||||
&& (((IElementalDamage)event.getSource()).getType() == MagicDamage.DamageType.WITHER))
|
||||
// or it's direct, non-melee damage and the player is holding a wand with a necromancy spell selected
|
||||
|| (event.getSource().getImmediateSource() == player && !WizardryUtilities.isMeleeDamage(event.getSource())
|
||||
&& Streams.stream(player.getHeldEquipment()).anyMatch(s -> s.getItem() instanceof ISpellCastingItem
|
||||
&& ((ISpellCastingItem)s.getItem()).getCurrentSpell(s).getElement() == Element.NECROMANCY))){
|
||||
|
||||
CurseOfSoulbinding.getSoulboundCreatures(WizardData.get(player)).add(event.getEntity().getUniqueID());
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_leeching){
|
||||
|
||||
// Best guess at necromancy spell damage: either it's wither damage...
|
||||
if(player.world.rand.nextFloat() < 0.3f && ((event.getSource() instanceof IElementalDamage
|
||||
&& (((IElementalDamage)event.getSource()).getType() == MagicDamage.DamageType.WITHER))
|
||||
// ...or it's direct, non-melee damage and the player is holding a wand with a necromancy spell selected
|
||||
|| (event.getSource().getImmediateSource() == player && !WizardryUtilities.isMeleeDamage(event.getSource())
|
||||
&& Streams.stream(player.getHeldEquipment()).anyMatch(s -> s.getItem() instanceof ISpellCastingItem
|
||||
&& ((ISpellCastingItem)s.getItem()).getCurrentSpell(s).getElement() == Element.NECROMANCY
|
||||
&& ((ISpellCastingItem)s.getItem()).getCurrentSpell(s) != Spells.life_drain)))){
|
||||
|
||||
if(player.shouldHeal()){
|
||||
player.heal(event.getAmount() * Spells.life_drain.getProperty(LifeDrain.HEAL_FACTOR).floatValue());
|
||||
}
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_poison){
|
||||
|
||||
// Best guess at earth spell damage: either it's poison damage...
|
||||
if((event.getSource() instanceof IElementalDamage
|
||||
&& (((IElementalDamage)event.getSource()).getType() == MagicDamage.DamageType.POISON))
|
||||
// ...or it was from a dart...
|
||||
|| event.getSource().getImmediateSource() instanceof EntityDart
|
||||
// ...or it's direct, non-melee damage and the player is holding a wand with an earth spell selected
|
||||
|| (event.getSource().getImmediateSource() == player && !WizardryUtilities.isMeleeDamage(event.getSource())
|
||||
&& Streams.stream(player.getHeldEquipment()).anyMatch(s -> s.getItem() instanceof ISpellCastingItem
|
||||
&& ((ISpellCastingItem)s.getItem()).getCurrentSpell(s).getElement() == Element.EARTH))){
|
||||
|
||||
event.getEntityLiving().addPotionEffect(new PotionEffect(MobEffects.POISON, 200, 0));
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_extraction){
|
||||
|
||||
// Best guess at sorcery spell damage: either it's force damage...
|
||||
if((event.getSource() instanceof IElementalDamage
|
||||
&& (((IElementalDamage)event.getSource()).getType() == MagicDamage.DamageType.FORCE))
|
||||
// ...or it was from a force orb...
|
||||
|| event.getSource().getImmediateSource() instanceof EntityForceOrb
|
||||
// ...or it's direct, non-melee damage and the player is holding a wand with a sorcery spell selected
|
||||
|| (event.getSource().getImmediateSource() == player && !WizardryUtilities.isMeleeDamage(event.getSource())
|
||||
&& Streams.stream(player.getHeldEquipment()).anyMatch(s -> s.getItem() instanceof ISpellCastingItem
|
||||
&& ((ISpellCastingItem)s.getItem()).getCurrentSpell(s).getElement() == Element.SORCERY))){
|
||||
|
||||
WizardryUtilities.getPrioritisedHotbarAndOffhand(player).stream()
|
||||
.filter(s -> s.getItem() instanceof ISpellCastingItem && s.getItem() instanceof IManaStoringItem
|
||||
&& !((IManaStoringItem)s.getItem()).isManaFull(s))
|
||||
.findFirst()
|
||||
.ifPresent(s -> ((IManaStoringItem)s.getItem()).rechargeMana(s, 4 + world.rand.nextInt(3)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingDeathEvent(LivingDeathEvent event){
|
||||
|
||||
if(event.getSource().getTrueSource() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getSource().getTrueSource();
|
||||
|
||||
for(ItemArtefact artefact : getActiveArtefacts(player)){
|
||||
|
||||
if(artefact == WizardryItems.ring_combustion){
|
||||
|
||||
if(event.getSource() instanceof IElementalDamage && ((IElementalDamage)event.getSource()).getType() == MagicDamage.DamageType.FIRE){
|
||||
event.getEntity().world.createExplosion(event.getEntity(), event.getEntity().posX, event.getEntity().posY,
|
||||
event.getEntity().posZ, 1.5f, false);
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_disintegration){
|
||||
|
||||
if(event.getSource() instanceof IElementalDamage && ((IElementalDamage)event.getSource()).getType() == MagicDamage.DamageType.FIRE){
|
||||
Disintegration.spawnEmbers(event.getEntity().world, player, event.getEntity(),
|
||||
Spells.disintegration.getProperty(Disintegration.EMBER_COUNT).intValue());
|
||||
}
|
||||
|
||||
}else if(artefact == WizardryItems.ring_arcane_frost){
|
||||
|
||||
if(!player.world.isRemote && event.getSource() instanceof IElementalDamage
|
||||
&& ((IElementalDamage)event.getSource()).getType() == MagicDamage.DamageType.FROST){
|
||||
|
||||
for(int i = 0; i < 8; i++){
|
||||
double dx = event.getEntity().world.rand.nextDouble() - 0.5;
|
||||
double dy = event.getEntity().world.rand.nextDouble() - 0.5;
|
||||
double dz = event.getEntity().world.rand.nextDouble() - 0.5;
|
||||
EntityIceShard iceshard = new EntityIceShard(event.getEntity().world);
|
||||
iceshard.setPosition(event.getEntity().posX + dx + Math.signum(dx) * event.getEntity().width,
|
||||
event.getEntity().posY + event.getEntity().height/2 + dy,
|
||||
event.getEntity().posZ + dz + Math.signum(dz) * event.getEntity().width);
|
||||
iceshard.motionX = dx * 1.5;
|
||||
iceshard.motionY = dy * 1.5;
|
||||
iceshard.motionZ = dz * 1.5;
|
||||
iceshard.setCaster(player);
|
||||
event.getEntity().world.spawnEntity(iceshard);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.HIGH) // Needs to happen before gravestones, etc.
|
||||
public static void onPlayerDropsEvent(PlayerDropsEvent event){
|
||||
// Amulet of the immortal allows players to hold onto a wand with resurrection
|
||||
// This needs to happen or we can't cast the spell with it and use up the mana
|
||||
if(isArtefactActive(event.getEntityPlayer(), WizardryItems.amulet_resurrection)){
|
||||
|
||||
EntityItem item = event.getDrops().stream()
|
||||
.filter(e -> Resurrection.canStackResurrect(e.getItem(), event.getEntityPlayer()))
|
||||
.findFirst().orElse(null);
|
||||
|
||||
if(item == null) return; // The player didn't have a wand with resurrection on it
|
||||
if(!WizardryUtilities.getHotbar(event.getEntityPlayer()).contains(ItemStack.EMPTY)) return; // No space on hotbar
|
||||
|
||||
event.getDrops().remove(item);
|
||||
// At this point the player probably has nothing in their hand, but if not just find a free space somewhere
|
||||
if(event.getEntityPlayer().getHeldItemMainhand().isEmpty()) event.getEntityPlayer().setHeldItem(EnumHand.MAIN_HAND, item.getItem());
|
||||
else event.getEntityPlayer().addItemStackToInventory(item.getItem()); // Always chooses hotbar slots first
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onPotionApplicableEvent(PotionEvent.PotionApplicableEvent event){
|
||||
|
||||
if(event.getEntity() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getEntity();
|
||||
|
||||
for(ItemArtefact artefact : getActiveArtefacts(player)){
|
||||
|
||||
if(artefact == WizardryItems.amulet_ice_immunity){
|
||||
|
||||
if(event.getPotionEffect().getPotion() == WizardryPotions.frost) event.setResult(Event.Result.DENY);
|
||||
|
||||
}else if(artefact == WizardryItems.amulet_wither_immunity){
|
||||
|
||||
if(event.getPotionEffect().getPotion() == MobEffects.WITHER) event.setResult(Event.Result.DENY);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onItemPickupEvent(PlayerEvent.ItemPickupEvent event){
|
||||
|
||||
// ItemPickupEvent is just a convenient trigger for this; we don't actually care what got picked up
|
||||
if(isArtefactActive(event.player, WizardryItems.charm_auto_smelt)){
|
||||
|
||||
// So this doesn't waste mana, only cast pocket furnace when it would smelt the maximum number of items
|
||||
if(event.player.inventory.mainInventory.stream()
|
||||
.filter(s -> !FurnaceRecipes.instance().getSmeltingResult(s).isEmpty())
|
||||
.mapToInt(ItemStack::getCount)
|
||||
.sum() >= Spells.pocket_furnace.getProperty(PocketFurnace.ITEMS_SMELTED).intValue()){
|
||||
|
||||
findMatchingWandAndCast(event.player, Spells.pocket_furnace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.SpellProperties;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.Slot;
|
||||
import net.minecraft.item.Item;
|
||||
@@ -22,28 +23,33 @@ public class ItemBlankScroll extends Item implements IWorkbenchItem {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showTooltip(ItemStack stack){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onApplyButtonPressed(EntityPlayer player, Slot centre, Slot crystals, Slot upgrade, Slot[] spellBooks){
|
||||
|
||||
if(!spellBooks[0].getStack().isEmpty() && !crystals.getStack().isEmpty()){
|
||||
|
||||
Spell spell = Spell.get(spellBooks[0].getStack().getItemDamage());
|
||||
Spell spell = Spell.byMetadata(spellBooks[0].getStack().getItemDamage());
|
||||
WizardData data = WizardData.get(player);
|
||||
|
||||
// Spells can only be bound to scrolls if the player has already cast them (prevents casting of master
|
||||
// spells without getting a master wand)
|
||||
// This restriction does not apply in creative mode
|
||||
if(spell != Spells.none && player.capabilities.isCreativeMode || (data != null
|
||||
&& data.hasSpellBeenDiscovered(spell))){
|
||||
if(spell != Spells.none && player.isCreative() || (data != null
|
||||
&& data.hasSpellBeenDiscovered(spell)) && spell.isEnabled(SpellProperties.Context.SCROLL)){
|
||||
|
||||
int cost = spell.cost;
|
||||
int cost = spell.getCost() * centre.getStack().getCount();
|
||||
// Continuous spell scrolls require enough mana to cast them for the duration defined in ItemScroll.
|
||||
if(spell.isContinuous) cost *= ItemScroll.CASTING_TIME / 20;
|
||||
|
||||
if(crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL > cost){
|
||||
// Rounds up to the nearest whole crystal
|
||||
crystals.decrStackSize(cost / Constants.MANA_PER_CRYSTAL + 1);
|
||||
centre.putStack(new ItemStack(WizardryItems.scroll, 1, spell.id()));
|
||||
centre.putStack(new ItemStack(WizardryItems.scroll, centre.getStack().getCount(), spell.metadata()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.item.ItemBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
public class ItemBlockMultiTexturedElemental extends ItemBlock implements IMultiTexturedItem {
|
||||
|
||||
private final boolean separateNames;
|
||||
|
||||
public ItemBlockMultiTexturedElemental(Block block, boolean separateNames){
|
||||
super(block);
|
||||
this.setHasSubtypes(true);
|
||||
this.setMaxDamage(0);
|
||||
this.separateNames = separateNames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getModelName(ItemStack stack){
|
||||
int metadata = stack.getMetadata();
|
||||
if(metadata >= Element.values().length) metadata = 0;
|
||||
return getModelName(metadata);
|
||||
}
|
||||
|
||||
public ResourceLocation getModelName(int metadata){
|
||||
return new ResourceLocation(this.block.getRegistryName().getNamespace(),
|
||||
Element.values()[metadata].getName() + "_" + this.block.getRegistryName().getPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTranslationKey(ItemStack stack){
|
||||
return this.separateNames ? "tile." + this.getModelName(stack).toString() : super.getTranslationKey(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetadata(int metadata){
|
||||
return metadata;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.NonNullList;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
/** Note that in 1.13, <i>the flattening</i> will make this class redundant, much like ItemCoal, which is probably its
|
||||
* closest analog in vanilla. */
|
||||
public class ItemCrystal extends Item implements IMultiTexturedItem {
|
||||
|
||||
public ItemCrystal(){
|
||||
super();
|
||||
this.setHasSubtypes(true);
|
||||
this.setMaxDamage(0);
|
||||
this.setCreativeTab(WizardryTabs.WIZARDRY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getModelName(ItemStack stack){
|
||||
int metadata = stack.getMetadata();
|
||||
if(metadata >= Element.values().length) metadata = 0;
|
||||
return new ResourceLocation(Wizardry.MODID, "crystal_" + Element.values()[metadata].getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTranslationKey(ItemStack stack){
|
||||
return "item." + this.getModelName(stack).toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> items){
|
||||
if(tab == WizardryTabs.WIZARDRY){
|
||||
for(Element element : Element.values()){
|
||||
items.add(new ItemStack(this, 1, element.ordinal()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.entity.projectile.EntityFirebomb;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ActionResult;
|
||||
@@ -23,15 +23,15 @@ public class ItemFirebomb extends Item {
|
||||
|
||||
ItemStack stack = player.getHeldItem(hand);
|
||||
|
||||
if(!player.capabilities.isCreativeMode){
|
||||
if(!player.isCreative()){
|
||||
stack.shrink(1);
|
||||
}
|
||||
|
||||
player.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F));
|
||||
player.playSound(WizardrySounds.ENTITY_FIREBOMB_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F));
|
||||
|
||||
if(!world.isRemote){
|
||||
EntityFirebomb firebomb = new EntityFirebomb(world);
|
||||
firebomb.aim(player, 1.5f);
|
||||
firebomb.aim(player, 1);
|
||||
world.spawnEntity(firebomb);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
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.ItemAxe;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
@@ -13,21 +22,42 @@ import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class ItemFlamingAxe extends ItemAxe implements IConjuredItem {
|
||||
|
||||
private EnumRarity rarity = EnumRarity.COMMON;
|
||||
|
||||
public ItemFlamingAxe(ToolMaterial material){
|
||||
super(material, 8, -3);
|
||||
setMaxDamage(getBaseDuration());
|
||||
setMaxDamage(1200); // Might cause problems if removed, the actual number is irrelevant as long as it's > 0
|
||||
setNoRepair();
|
||||
setCreativeTab(null);
|
||||
addAnimationPropertyOverrides();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseDuration(){
|
||||
return 1200;
|
||||
public Multimap<String, AttributeModifier> getAttributeModifiers(EntityEquipmentSlot slot, ItemStack stack){
|
||||
|
||||
Multimap<String, AttributeModifier> multimap = super.getItemAttributeModifiers(slot);
|
||||
|
||||
if(slot == EntityEquipmentSlot.MAINHAND){
|
||||
multimap.put(SharedMonsterAttributes.ATTACK_DAMAGE.getName(), new AttributeModifier(POTENCY_MODIFIER,
|
||||
"Potency modifier", IConjuredItem.getDamageMultiplier(stack) - 1, WizardryUtilities.Operations.MULTIPLY_CUMULATIVE));
|
||||
}
|
||||
|
||||
return multimap;
|
||||
}
|
||||
|
||||
public Item setRarity(EnumRarity rarity){
|
||||
this.rarity = rarity;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumRarity getRarity(ItemStack stack){
|
||||
return rarity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxDamage(ItemStack stack){
|
||||
return this.getMaxDamageFromNBT(stack);
|
||||
return this.getMaxDamageFromNBT(stack, Spells.flaming_axe);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,9 +80,16 @@ public class ItemFlamingAxe extends ItemAxe implements IConjuredItem {
|
||||
stack.setItemDamage(damage + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Multimap<String, AttributeModifier> getItemAttributeModifiers(EntityEquipmentSlot equipmentSlot){
|
||||
attackDamage = Spells.flaming_axe.getProperty(Spell.DAMAGE).floatValue();
|
||||
return super.getItemAttributeModifiers(equipmentSlot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hitEntity(ItemStack stack, EntityLivingBase target, EntityLivingBase wielder){
|
||||
if(!MagicDamage.isEntityImmune(DamageType.FIRE, target)) target.setFire(8);
|
||||
if(!MagicDamage.isEntityImmune(DamageType.FIRE, target))
|
||||
target.setFire(Spells.flaming_axe.getProperty(Spell.BURN_DURATION).intValue());
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -72,6 +109,16 @@ public class ItemFlamingAxe extends ItemAxe implements IConjuredItem {
|
||||
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){
|
||||
|
||||
@@ -1,11 +1,19 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.util.MagicDamage;
|
||||
import electroblob.wizardry.util.MagicDamage.DamageType;
|
||||
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.ItemAxe;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.potion.PotionEffect;
|
||||
@@ -15,21 +23,42 @@ import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class ItemFrostAxe extends ItemAxe implements IConjuredItem {
|
||||
|
||||
private EnumRarity rarity = EnumRarity.COMMON;
|
||||
|
||||
public ItemFrostAxe(ToolMaterial material){
|
||||
super(material, 8, -3);
|
||||
setMaxDamage(getBaseDuration());
|
||||
setMaxDamage(1200);
|
||||
setNoRepair();
|
||||
setCreativeTab(null);
|
||||
addAnimationPropertyOverrides();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseDuration(){
|
||||
return 1200;
|
||||
public Multimap<String, AttributeModifier> getAttributeModifiers(EntityEquipmentSlot slot, ItemStack stack){
|
||||
|
||||
Multimap<String, AttributeModifier> multimap = super.getItemAttributeModifiers(slot);
|
||||
|
||||
if(slot == EntityEquipmentSlot.MAINHAND){
|
||||
multimap.put(SharedMonsterAttributes.ATTACK_DAMAGE.getName(), new AttributeModifier(POTENCY_MODIFIER,
|
||||
"Potency modifier", IConjuredItem.getDamageMultiplier(stack) - 1, WizardryUtilities.Operations.MULTIPLY_CUMULATIVE));
|
||||
}
|
||||
|
||||
return multimap;
|
||||
}
|
||||
|
||||
public Item setRarity(EnumRarity rarity){
|
||||
this.rarity = rarity;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumRarity getRarity(ItemStack stack){
|
||||
return rarity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxDamage(ItemStack stack){
|
||||
return this.getMaxDamageFromNBT(stack);
|
||||
return this.getMaxDamageFromNBT(stack, Spells.frost_axe);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -75,6 +104,16 @@ public class ItemFrostAxe extends ItemAxe implements IConjuredItem {
|
||||
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){
|
||||
|
||||
@@ -1,19 +1,14 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.event.DiscoverSpellEvent;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.util.ITooltipFlag;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.EnumRarity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ActionResult;
|
||||
@@ -25,6 +20,9 @@ import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public class ItemIdentificationScroll extends Item {
|
||||
|
||||
public ItemIdentificationScroll(){
|
||||
@@ -38,11 +36,15 @@ public class ItemIdentificationScroll extends Item {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumRarity getRarity(ItemStack stack){
|
||||
return EnumRarity.UNCOMMON;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void addInformation(ItemStack stack, @Nullable World worldIn, List<String> tooltip, ITooltipFlag flagIn) {
|
||||
tooltip.add(net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":identification_scroll.desc1", "\u00A77"));
|
||||
tooltip.add(net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":identification_scroll.desc2", "\u00A77"));
|
||||
public void addInformation(ItemStack stack, @Nullable World world, List<String> tooltip, net.minecraft.client.util.ITooltipFlag flag) {
|
||||
Wizardry.proxy.addMultiLineDescription(tooltip, "item." + this.getRegistryName() + ".desc");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,27 +54,26 @@ public class ItemIdentificationScroll extends Item {
|
||||
|
||||
if(WizardData.get(player) != null){
|
||||
|
||||
WizardData properties = WizardData.get(player);
|
||||
WizardData data = WizardData.get(player);
|
||||
|
||||
for(ItemStack stack1 : WizardryUtilities.getPrioritisedHotbarAndOffhand(player)){
|
||||
|
||||
if(!stack1.isEmpty()){
|
||||
Spell spell = Spell.get(stack1.getItemDamage());
|
||||
Spell spell = Spell.byMetadata(stack1.getItemDamage());
|
||||
if((stack1.getItem() instanceof ItemSpellBook || stack1.getItem() instanceof ItemScroll)
|
||||
&& !properties.hasSpellBeenDiscovered(spell)){
|
||||
&& !data.hasSpellBeenDiscovered(spell)){
|
||||
|
||||
if(!MinecraftForge.EVENT_BUS.post(new DiscoverSpellEvent(player, spell,
|
||||
DiscoverSpellEvent.Source.IDENTIFICATION_SCROLL))){
|
||||
// Identification scrolls give the chat readout in creative mode, otherwise it looks like
|
||||
// nothing happens!
|
||||
properties.discoverSpell(spell);
|
||||
WizardryAdvancementTriggers.identify_spell.triggerFor(player);
|
||||
player.playSound(SoundEvents.ENTITY_PLAYER_LEVELUP, 1.25f, 1);
|
||||
if(!player.capabilities.isCreativeMode) stack.shrink(1);
|
||||
data.discoverSpell(spell);
|
||||
player.playSound(WizardrySounds.MISC_DISCOVER_SPELL, 1.25f, 1);
|
||||
if(!player.isCreative()) stack.shrink(1);
|
||||
if(!world.isRemote) player.sendMessage(new TextComponentTranslation("spell.discover",
|
||||
spell.getNameForTranslationFormatted()));
|
||||
|
||||
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
return new ActionResult<>(EnumActionResult.SUCCESS, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,7 +83,7 @@ public class ItemIdentificationScroll extends Item {
|
||||
new TextComponentTranslation("item." + Wizardry.MODID + ":identification_scroll.nothing_to_identify"));
|
||||
}
|
||||
|
||||
return new ActionResult<ItemStack>(EnumActionResult.FAIL, stack);
|
||||
return new ActionResult<>(EnumActionResult.FAIL, stack);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import net.minecraft.item.EnumRarity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
|
||||
public class ItemManaFlask extends Item {
|
||||
|
||||
public enum Size {
|
||||
|
||||
SMALL(75, EnumRarity.COMMON),
|
||||
MEDIUM(700, EnumRarity.COMMON),
|
||||
LARGE(1400, EnumRarity.RARE);
|
||||
|
||||
public int capacity;
|
||||
public EnumRarity rarity;
|
||||
|
||||
Size(int capacity, EnumRarity rarity){
|
||||
this.capacity = capacity;
|
||||
this.rarity = rarity;
|
||||
}
|
||||
}
|
||||
|
||||
public final Size size;
|
||||
|
||||
public ItemManaFlask(Size size){
|
||||
super();
|
||||
this.size = size;
|
||||
this.setCreativeTab(WizardryTabs.WIZARDRY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumRarity getRarity(ItemStack stack){
|
||||
return size.rarity;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.entity.projectile.EntityPoisonBomb;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ActionResult;
|
||||
@@ -23,15 +23,15 @@ public class ItemPoisonBomb extends Item {
|
||||
|
||||
ItemStack stack = player.getHeldItem(hand);
|
||||
|
||||
if(!player.capabilities.isCreativeMode){
|
||||
if(!player.isCreative()){
|
||||
stack.shrink(1);
|
||||
}
|
||||
|
||||
player.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F));
|
||||
player.playSound(WizardrySounds.ENTITY_POISON_BOMB_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F));
|
||||
|
||||
if(!world.isRemote){
|
||||
EntityPoisonBomb poisonbomb = new EntityPoisonBomb(world);
|
||||
poisonbomb.aim(player, 1.5f);
|
||||
poisonbomb.aim(player, 1);
|
||||
world.spawnEntity(poisonbomb);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import electroblob.wizardry.util.ParticleBuilder;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import net.minecraft.advancements.CriteriaTriggers;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.entity.player.EntityPlayerMP;
|
||||
import net.minecraft.init.Items;
|
||||
import net.minecraft.item.EnumAction;
|
||||
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.SoundCategory;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public class ItemPurifyingElixir extends Item {
|
||||
|
||||
public ItemPurifyingElixir(){
|
||||
this.setMaxStackSize(1);
|
||||
this.setCreativeTab(WizardryTabs.WIZARDRY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEffect(ItemStack stack){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumRarity getRarity(ItemStack stack){
|
||||
return EnumRarity.RARE;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void addInformation(ItemStack stack, @Nullable World world, List<String> tooltip, net.minecraft.client.util.ITooltipFlag flag) {
|
||||
Wizardry.proxy.addMultiLineDescription(tooltip, "item." + this.getRegistryName() + ".desc");
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack onItemUseFinish(ItemStack stack, World world, EntityLivingBase entity){
|
||||
|
||||
if(!world.isRemote){
|
||||
entity.curePotionEffects(stack);
|
||||
}else{
|
||||
|
||||
ParticleBuilder.spawnHealParticles(world, entity);
|
||||
|
||||
for(int i = 0; i < 20; i++){
|
||||
double x = entity.posX + world.rand.nextDouble() * 2 - 1;
|
||||
double y = entity.getEntityBoundingBox().minY + entity.getEyeHeight() - 0.5 + world.rand.nextDouble();
|
||||
double z = entity.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);
|
||||
}
|
||||
}
|
||||
|
||||
world.playSound(entity.posX, entity.posY, entity.posZ, WizardrySounds.ITEM_PURIFYING_ELIXIR_DRINK, SoundCategory.PLAYERS, 1, 1, false);
|
||||
|
||||
if(entity instanceof EntityPlayerMP){
|
||||
EntityPlayerMP entityplayermp = (EntityPlayerMP)entity;
|
||||
CriteriaTriggers.CONSUME_ITEM.trigger(entityplayermp, stack);
|
||||
}
|
||||
|
||||
if(entity instanceof EntityPlayer && !((EntityPlayer)entity).capabilities.isCreativeMode){
|
||||
stack.shrink(1);
|
||||
}
|
||||
|
||||
return stack.isEmpty() ? new ItemStack(Items.GLASS_BOTTLE) : stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxItemUseDuration(ItemStack stack){
|
||||
return 32;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumAction getItemUseAction(ItemStack stack){
|
||||
return EnumAction.DRINK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult<ItemStack> onItemRightClick(World worldIn, EntityPlayer playerIn, EnumHand handIn){
|
||||
playerIn.setActiveHand(handIn);
|
||||
return new ActionResult<>(EnumActionResult.SUCCESS, playerIn.getHeldItem(handIn));
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -24,10 +23,9 @@ import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class ItemScroll extends Item {
|
||||
public class ItemScroll extends Item implements ISpellCastingItem {
|
||||
|
||||
/** The maximum number of ticks a continuous spell scroll can be cast for (by holding the use item button). */
|
||||
// TODO: Make this configurable
|
||||
public static final int CASTING_TIME = 120;
|
||||
|
||||
public ItemScroll(){
|
||||
@@ -36,14 +34,24 @@ public class ItemScroll extends Item {
|
||||
setMaxStackSize(16);
|
||||
setCreativeTab(WizardryTabs.SPELLS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spell getCurrentSpell(ItemStack stack){
|
||||
return Spell.byMetadata(stack.getItemDamage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showSpellHUD(EntityPlayer player, ItemStack stack){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> list){
|
||||
if(tab == WizardryTabs.SPELLS){
|
||||
// In this particular case, getTotalSpellCount() is a more efficient way of doing this since the spell instance
|
||||
// is not required, only the id.
|
||||
// is not required, only the metadata.
|
||||
for(int i = 0; i < Spell.getTotalSpellCount(); i++){
|
||||
// i+1 is used so that the metadata ties up with the id() method. In other words, the none spell has id
|
||||
// i+1 is used so that the metadata ties up with the metadata() method. In other words, the none spell has metadata
|
||||
// 0 and since this is not used as a spell book the metadata starts at 1.
|
||||
list.add(new ItemStack(this, 1, i + 1));
|
||||
}
|
||||
@@ -83,54 +91,25 @@ public class ItemScroll extends Item {
|
||||
|
||||
ItemStack stack = player.getHeldItem(hand);
|
||||
|
||||
Spell spell = Spell.get(stack.getItemDamage());
|
||||
Spell spell = Spell.byMetadata(stack.getItemDamage());
|
||||
// By default, scrolls have no modifiers - but with the event system, they could be added.
|
||||
SpellModifiers modifiers = new SpellModifiers();
|
||||
|
||||
// If anything stops the spell working at this point, nothing else happens.
|
||||
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Pre(player, spell, modifiers, Source.SCROLL))){
|
||||
return new ActionResult<ItemStack>(EnumActionResult.FAIL, stack);
|
||||
}
|
||||
|
||||
// Now we can cast continuous spells with scrolls!
|
||||
if(spell.isContinuous){
|
||||
if(!player.isHandActive()){
|
||||
player.setActiveHand(hand);
|
||||
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
}
|
||||
}else{
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
if(spell.cast(world, player, hand, 0, new SpellModifiers())){
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.SCROLL));
|
||||
|
||||
if(spell.doesSpellRequirePacket()){
|
||||
// Sends a packet to all players in dimension to tell them to spawn particles.
|
||||
IMessage msg = new PacketCastSpell.Message(player.getEntityId(), hand, spell.id(), modifiers);
|
||||
WizardryPacketHandler.net.sendToDimension(msg, world.provider.getDimension());
|
||||
}
|
||||
|
||||
// Scrolls are consumed upon successful use in survival mode
|
||||
if(!player.capabilities.isCreativeMode) stack.shrink(1);
|
||||
|
||||
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
if(canCast(stack, spell, player, hand, 0, modifiers)){
|
||||
// Now we can cast continuous spells with scrolls!
|
||||
if(spell.isContinuous){
|
||||
if(!player.isHandActive()){
|
||||
player.setActiveHand(hand);
|
||||
return new ActionResult<>(EnumActionResult.SUCCESS, stack);
|
||||
}
|
||||
|
||||
// This else if check was bugging me for AGES! I can't believe I didn't compare to ItemWand before.
|
||||
}else if(!spell.doesSpellRequirePacket()){
|
||||
// Client-inconsistent spell casting. This code only runs client-side.
|
||||
if(spell.cast(world, player, hand, 0, modifiers)){
|
||||
// This is all that needs to happen, because everything above works fine on just the server side.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.SCROLL));
|
||||
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
}else{
|
||||
if(cast(stack, spell, player, hand, 0, modifiers)){
|
||||
return new ActionResult<>(EnumActionResult.SUCCESS, stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ActionResult<ItemStack>(EnumActionResult.FAIL, stack);
|
||||
|
||||
return new ActionResult<>(EnumActionResult.FAIL, stack);
|
||||
}
|
||||
|
||||
// For continuous spells. The count argument actually decrements by 1 each tick.
|
||||
@@ -141,31 +120,68 @@ public class ItemScroll extends Item {
|
||||
|
||||
EntityPlayer player = (EntityPlayer)user;
|
||||
|
||||
Spell spell = Spell.get(stack.getItemDamage());
|
||||
Spell spell = Spell.byMetadata(stack.getItemDamage());
|
||||
// By default, scrolls have no modifiers - but with the event system, they could be added.
|
||||
SpellModifiers modifiers = new SpellModifiers();
|
||||
int castingTick = stack.getMaxItemUseDuration() - count;
|
||||
|
||||
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(Source.SCROLL, spell, player, modifiers, castingTick)))
|
||||
return;
|
||||
|
||||
// Continuous spells (these must check if they can be cast each tick since the mana changes)
|
||||
if(spell.isContinuous){
|
||||
|
||||
if(spell.cast(player.world, player, player.getActiveHand(), castingTick, modifiers)){
|
||||
|
||||
if(castingTick == 0)
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(Source.SCROLL, spell, player, modifiers));
|
||||
}
|
||||
// In theory the spell is always continuous here but just in case it isn't...
|
||||
if(spell.isContinuous && canCast(stack, spell, player, player.getActiveHand(), castingTick, modifiers)){
|
||||
cast(stack, spell, player, player.getActiveHand(), castingTick, modifiers);
|
||||
}else{
|
||||
// Scrolls normally work on the max use duration so this isn't ever reached by wizardry, but if the
|
||||
// casting was interrupted by SpellCastEvent.Tick it will be used
|
||||
player.stopActiveHand();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canCast(ItemStack stack, Spell spell, EntityPlayer caster, EnumHand hand, int castingTick, SpellModifiers modifiers){
|
||||
// Even neater!
|
||||
if(castingTick == 0){
|
||||
return !MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Pre(Source.SCROLL, spell, caster, modifiers));
|
||||
}else{
|
||||
return !MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(Source.SCROLL, spell, caster, modifiers, castingTick));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean cast(ItemStack stack, Spell spell, EntityPlayer caster, EnumHand hand, int castingTick, SpellModifiers modifiers){
|
||||
|
||||
World world = caster.world;
|
||||
|
||||
if(world.isRemote && !spell.isContinuous && spell.requiresPacket()) return false;
|
||||
|
||||
if(spell.cast(world, caster, hand, castingTick, modifiers)){
|
||||
|
||||
if(castingTick == 0) MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(Source.SCROLL, spell, caster, modifiers));
|
||||
|
||||
if(!world.isRemote){
|
||||
|
||||
// Continuous spells never require packets so don't rely on the requiresPacket method to specify it
|
||||
if(!spell.isContinuous && spell.requiresPacket()){
|
||||
// Sends a packet to all players in dimension to tell them to spawn particles.
|
||||
IMessage msg = new PacketCastSpell.Message(caster.getEntityId(), hand, spell, modifiers);
|
||||
WizardryPacketHandler.net.sendToDimension(msg, world.provider.getDimension());
|
||||
}
|
||||
|
||||
// Scrolls are consumed upon successful use in survival mode
|
||||
if(!spell.isContinuous && !caster.isCreative()) stack.shrink(1);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStoppedUsing(ItemStack stack, World world, EntityLivingBase user, int timeLeft){
|
||||
// Consumes a continuous spell scroll when a player in survival mode stops using it.
|
||||
if(Spell.get(stack.getItemDamage()).isContinuous
|
||||
&& (!(user instanceof EntityPlayer) || !((EntityPlayer)user).capabilities.isCreativeMode)){
|
||||
if(Spell.byMetadata(stack.getItemDamage()).isContinuous
|
||||
&& (!(user instanceof EntityPlayer) || !((EntityPlayer)user).isCreative())){
|
||||
stack.shrink(1);
|
||||
}
|
||||
}
|
||||
@@ -173,8 +189,8 @@ public class ItemScroll extends Item {
|
||||
@Override
|
||||
public ItemStack onItemUseFinish(ItemStack stack, World world, EntityLivingBase user){
|
||||
// Consumes a continuous spell scroll when the casting elapses whilst in use by a player in survival mode.
|
||||
if(Spell.get(stack.getItemDamage()).isContinuous
|
||||
&& (!(user instanceof EntityPlayer) || !((EntityPlayer)user).capabilities.isCreativeMode)){
|
||||
if(Spell.byMetadata(stack.getItemDamage()).isContinuous
|
||||
&& (!(user instanceof EntityPlayer) || !((EntityPlayer)user).isCreative())){
|
||||
stack.shrink(1);
|
||||
}
|
||||
|
||||
@@ -183,7 +199,7 @@ public class ItemScroll extends Item {
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public FontRenderer getFontRenderer(ItemStack stack){
|
||||
public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){
|
||||
return Wizardry.proxy.getFontRenderer(stack);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.entity.projectile.EntitySmokeBomb;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.init.SoundEvents;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ActionResult;
|
||||
@@ -23,15 +23,15 @@ public class ItemSmokeBomb extends Item {
|
||||
|
||||
ItemStack stack = player.getHeldItem(hand);
|
||||
|
||||
if(!player.capabilities.isCreativeMode){
|
||||
if(!player.isCreative()){
|
||||
stack.shrink(1);
|
||||
}
|
||||
|
||||
player.playSound(SoundEvents.ENTITY_SNOWBALL_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F));
|
||||
player.playSound(WizardrySounds.ENTITY_SMOKE_BOMB_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F));
|
||||
|
||||
if(!world.isRemote){
|
||||
EntitySmokeBomb smokebomb = new EntitySmokeBomb(world);
|
||||
smokebomb.aim(player, 1.5f);
|
||||
smokebomb.aim(player, 1);
|
||||
world.spawnEntity(smokebomb);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.entity.projectile.EntitySparkBomb;
|
||||
import electroblob.wizardry.registry.WizardrySounds;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
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.world.World;
|
||||
|
||||
public class ItemSparkBomb extends Item {
|
||||
|
||||
public ItemSparkBomb(){
|
||||
setMaxStackSize(16);
|
||||
setCreativeTab(WizardryTabs.WIZARDRY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer player, EnumHand hand){
|
||||
|
||||
ItemStack stack = player.getHeldItem(hand);
|
||||
|
||||
if(!player.isCreative()){
|
||||
stack.shrink(1);
|
||||
}
|
||||
|
||||
player.playSound(WizardrySounds.ENTITY_SPARK_BOMB_THROW, 0.5F, 0.4F / (itemRand.nextFloat() * 0.4F + 0.8F));
|
||||
|
||||
if(!world.isRemote){
|
||||
EntitySparkBomb sparkBomb = new EntitySparkBomb(world);
|
||||
sparkBomb.aim(player, 1);
|
||||
world.spawnEntity(sparkBomb);
|
||||
}
|
||||
|
||||
return ActionResult.newResult(EnumActionResult.SUCCESS, stack);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import net.minecraft.client.model.ModelBiped;
|
||||
import net.minecraft.client.renderer.GlStateManager;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
@@ -18,17 +17,12 @@ public class ItemSpectralArmour extends ItemArmor implements IConjuredItem {
|
||||
public ItemSpectralArmour(ArmorMaterial material, int renderIndex, EntityEquipmentSlot armourType){
|
||||
super(material, renderIndex, armourType);
|
||||
setCreativeTab(null);
|
||||
setMaxDamage(getBaseDuration());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseDuration(){
|
||||
return 1800;
|
||||
setMaxDamage(1200);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxDamage(ItemStack stack){
|
||||
return this.getMaxDamageFromNBT(stack);
|
||||
return this.getMaxDamageFromNBT(stack, Spells.conjure_armour);
|
||||
}
|
||||
|
||||
// Overridden to stop the enchantment trick making the name turn blue.
|
||||
@@ -73,6 +67,16 @@ public class ItemSpectralArmour extends ItemArmor implements IConjuredItem {
|
||||
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){
|
||||
@@ -89,14 +93,14 @@ public class ItemSpectralArmour extends ItemArmor implements IConjuredItem {
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack,
|
||||
EntityEquipmentSlot armorSlot, ModelBiped _default){
|
||||
GlStateManager.enableBlend();
|
||||
GlStateManager.tryBlendFuncSeparate(
|
||||
GlStateManager.SourceFactor.SRC_ALPHA,
|
||||
GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA,
|
||||
GlStateManager.SourceFactor.ONE,
|
||||
GlStateManager.DestFactor.ZERO
|
||||
public net.minecraft.client.model.ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack,
|
||||
EntityEquipmentSlot armorSlot, net.minecraft.client.model.ModelBiped _default){
|
||||
net.minecraft.client.renderer.GlStateManager.enableBlend();
|
||||
net.minecraft.client.renderer.GlStateManager.tryBlendFuncSeparate(
|
||||
net.minecraft.client.renderer.GlStateManager.SourceFactor.SRC_ALPHA,
|
||||
net.minecraft.client.renderer.GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA,
|
||||
net.minecraft.client.renderer.GlStateManager.SourceFactor.ONE,
|
||||
net.minecraft.client.renderer.GlStateManager.DestFactor.ZERO
|
||||
);
|
||||
return super.getArmorModel(entityLiving, itemStack, armorSlot, _default);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLivingBase;
|
||||
@@ -17,20 +15,18 @@ import net.minecraft.item.ItemArrow;
|
||||
import net.minecraft.item.ItemBow;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.stats.StatList;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.EnumActionResult;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.*;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ItemSpectralBow extends ItemBow implements IConjuredItem {
|
||||
|
||||
public ItemSpectralBow(){
|
||||
super();
|
||||
setMaxDamage(getBaseDuration());
|
||||
setMaxDamage(1200);
|
||||
setNoRepair();
|
||||
setCreativeTab(null);
|
||||
this.addPropertyOverride(new ResourceLocation("pull"), new IItemPropertyGetter(){
|
||||
@@ -40,7 +36,6 @@ public class ItemSpectralBow extends ItemBow implements IConjuredItem {
|
||||
return 0.0F;
|
||||
}else{
|
||||
ItemStack itemstack = entityIn.getActiveItemStack();
|
||||
// Mojang, observe - hardcoding item references into their own classes is NOT good Java.
|
||||
return itemstack.getItem() == ItemSpectralBow.this
|
||||
? (float)(stack.getMaxItemUseDuration() - entityIn.getItemInUseCount()) / 20.0F
|
||||
: 0.0F;
|
||||
@@ -54,6 +49,7 @@ public class ItemSpectralBow extends ItemBow implements IConjuredItem {
|
||||
: 0.0F;
|
||||
}
|
||||
});
|
||||
addAnimationPropertyOverrides();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,14 +59,9 @@ public class ItemSpectralBow extends ItemBow implements IConjuredItem {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseDuration(){
|
||||
return 1200;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxDamage(ItemStack stack){
|
||||
return this.getMaxDamageFromNBT(stack);
|
||||
return this.getMaxDamageFromNBT(stack, Spells.conjure_bow);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,27 +69,38 @@ public class ItemSpectralBow extends ItemBow implements IConjuredItem {
|
||||
// onUpdate() and removing the workaround that involved WizardData and all sorts of crazy stuff.
|
||||
public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){
|
||||
|
||||
// TODO: For some reason there used to be an && here instead of an ||, which makes me wonder if there's a weird
|
||||
// fix I did that needs removing.
|
||||
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.
|
||||
&& !Minecraft.getMinecraft().player.isHandActive())
|
||||
&& !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);
|
||||
// Can't damage it whilst in use because for some reason it causes the item use to constantly reset.
|
||||
if(!(entity instanceof EntityLivingBase) || !((EntityLivingBase)entity).isHandActive()){
|
||||
stack.setItemDamage(damage + 1);
|
||||
}
|
||||
stack.setItemDamage(damage + 1);
|
||||
}
|
||||
|
||||
// The following two methods re-route the displayed durability through the proxies in order to override the pausing
|
||||
@@ -145,19 +147,29 @@ public class ItemSpectralBow extends ItemBow implements IConjuredItem {
|
||||
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 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){
|
||||
@@ -205,10 +217,12 @@ public class ItemSpectralBow extends ItemBow implements IConjuredItem {
|
||||
|
||||
entityarrow.pickupStatus = EntityArrow.PickupStatus.DISALLOWED;
|
||||
|
||||
entityarrow.setDamage(entityarrow.getDamage() * IConjuredItem.getDamageMultiplier(stack));
|
||||
|
||||
world.spawnEntity(entityarrow);
|
||||
}
|
||||
|
||||
world.playSound((EntityPlayer)null, entityplayer.posX, entityplayer.posY, entityplayer.posZ,
|
||||
world.playSound(null, entityplayer.posX, entityplayer.posY, entityplayer.posZ,
|
||||
SoundEvents.ENTITY_ARROW_SHOOT, SoundCategory.NEUTRAL, 1.0F,
|
||||
1.0F / (itemRand.nextFloat() * 0.4F + 1.2F) + f * 0.5F);
|
||||
|
||||
|
||||
@@ -1,30 +1,44 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import net.minecraft.block.state.IBlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.EnumRarity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemPickaxe;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ItemSpectralPickaxe extends ItemPickaxe implements IConjuredItem {
|
||||
|
||||
private EnumRarity rarity = EnumRarity.COMMON;
|
||||
|
||||
public ItemSpectralPickaxe(ToolMaterial material){
|
||||
super(material);
|
||||
setMaxDamage(getBaseDuration());
|
||||
setMaxDamage(1200);
|
||||
setNoRepair();
|
||||
setCreativeTab(null);
|
||||
addAnimationPropertyOverrides();
|
||||
}
|
||||
|
||||
public Item setRarity(EnumRarity rarity){
|
||||
this.rarity = rarity;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseDuration(){
|
||||
return 1200;
|
||||
public EnumRarity getRarity(ItemStack stack){
|
||||
return rarity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxDamage(ItemStack stack){
|
||||
return this.getMaxDamageFromNBT(stack);
|
||||
return this.getMaxDamageFromNBT(stack, Spells.conjure_pickaxe);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,6 +61,18 @@ public class ItemSpectralPickaxe extends ItemPickaxe implements IConjuredItem {
|
||||
stack.setItemDamage(damage + 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getDestroySpeed(ItemStack stack, IBlockState state){
|
||||
float speed = super.getDestroySpeed(stack, state);
|
||||
return speed > 1 ? speed * IConjuredItem.getDamageMultiplier(stack) : speed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHarvestLevel(ItemStack stack, String toolClass, @Nullable EntityPlayer player, @Nullable IBlockState blockState){
|
||||
// Reuses the standard bonus amplifier calculation from SpellBuff to increase the mining level at advanced and master tier
|
||||
return super.getHarvestLevel(stack, toolClass, player, blockState) + (int)((IConjuredItem.getDamageMultiplier(stack) - 1) / 0.4);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public boolean hasEffect(ItemStack stack){
|
||||
@@ -63,10 +89,19 @@ public class ItemSpectralPickaxe extends ItemPickaxe implements IConjuredItem {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import com.google.common.collect.Multimap;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.entity.Entity;
|
||||
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.item.ItemSword;
|
||||
import net.minecraft.world.World;
|
||||
@@ -10,21 +18,42 @@ import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
public class ItemSpectralSword extends ItemSword implements IConjuredItem {
|
||||
|
||||
private EnumRarity rarity = EnumRarity.COMMON;
|
||||
|
||||
public ItemSpectralSword(ToolMaterial material){
|
||||
super(material);
|
||||
setMaxDamage(getBaseDuration());
|
||||
setMaxDamage(1200);
|
||||
setNoRepair();
|
||||
setCreativeTab(null);
|
||||
addAnimationPropertyOverrides();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseDuration(){
|
||||
return 1200;
|
||||
public Multimap<String, AttributeModifier> getAttributeModifiers(EntityEquipmentSlot slot, ItemStack stack){
|
||||
|
||||
Multimap<String, AttributeModifier> multimap = super.getItemAttributeModifiers(slot);
|
||||
|
||||
if(slot == EntityEquipmentSlot.MAINHAND){
|
||||
multimap.put(SharedMonsterAttributes.ATTACK_DAMAGE.getName(), new AttributeModifier(POTENCY_MODIFIER,
|
||||
"Potency modifier", IConjuredItem.getDamageMultiplier(stack) - 1, WizardryUtilities.Operations.MULTIPLY_CUMULATIVE));
|
||||
}
|
||||
|
||||
return multimap;
|
||||
}
|
||||
|
||||
public Item setRarity(EnumRarity rarity){
|
||||
this.rarity = rarity;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumRarity getRarity(ItemStack stack){
|
||||
return rarity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxDamage(ItemStack stack){
|
||||
return this.getMaxDamageFromNBT(stack);
|
||||
return this.getMaxDamageFromNBT(stack, Spells.conjure_sword);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -63,6 +92,16 @@ public class ItemSpectralSword extends ItemSword implements IConjuredItem {
|
||||
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){
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.SpellGlyphData;
|
||||
import electroblob.wizardry.WizardData;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.WizardryGuiHandler;
|
||||
import electroblob.wizardry.data.SpellGlyphData;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.util.ITooltipFlag;
|
||||
import net.minecraft.creativetab.CreativeTabs;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.Item;
|
||||
@@ -25,6 +18,8 @@ import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
import net.minecraftforge.oredict.OreDictionary;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class ItemSpellBook extends Item {
|
||||
|
||||
public ItemSpellBook(){
|
||||
@@ -38,9 +33,9 @@ public class ItemSpellBook extends Item {
|
||||
public void getSubItems(CreativeTabs tab, NonNullList<ItemStack> list){
|
||||
if(tab == WizardryTabs.SPELLS){
|
||||
// In this particular case, getTotalSpellCount() is a more efficient way of doing this since the spell instance
|
||||
// is not required, only the id.
|
||||
// is not required, only the metadata.
|
||||
for(int i = 0; i < Spell.getTotalSpellCount(); i++){
|
||||
// i+1 is used so that the metadata ties up with the id() method. In other words, the none spell has id
|
||||
// i+1 is used so that the metadata ties up with the metadata() method. In other words, the none spell has metadata
|
||||
// 0 and since this is not used as a spell book the metadata starts at 1.
|
||||
list.add(new ItemStack(this, 1, i + 1));
|
||||
}
|
||||
@@ -56,23 +51,19 @@ public class ItemSpellBook extends Item {
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void addInformation(ItemStack itemstack, World world, List<String> tooltip, ITooltipFlag advanced){
|
||||
public void addInformation(ItemStack itemstack, World world, List<String> tooltip, net.minecraft.client.util.ITooltipFlag advanced){
|
||||
// Tooltip is left blank for wizards buying generic spell books.
|
||||
if(itemstack.getItemDamage() != OreDictionary.WILDCARD_VALUE){
|
||||
EntityPlayerSP player = Minecraft.getMinecraft().player;
|
||||
if(world != null && itemstack.getItemDamage() != OreDictionary.WILDCARD_VALUE){
|
||||
|
||||
Spell spell = Spell.get(itemstack.getItemDamage());
|
||||
Spell spell = Spell.byMetadata(itemstack.getItemDamage());
|
||||
|
||||
boolean discovered = true;
|
||||
if(player != null && Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode && WizardData.get(player) != null
|
||||
&& !WizardData.get(player).hasSpellBeenDiscovered(spell)){
|
||||
discovered = false;
|
||||
}
|
||||
boolean discovered = Wizardry.proxy.shouldDisplayDiscovered(spell, itemstack);
|
||||
|
||||
// Element colour is not given for undiscovered spells
|
||||
tooltip.add(discovered ? "\u00A77" + spell.getDisplayNameWithFormatting()
|
||||
: "#\u00A79" + SpellGlyphData.getGlyphName(spell, player.world));
|
||||
tooltip.add(spell.tier.getDisplayNameWithFormatting());
|
||||
: "#\u00A79" + SpellGlyphData.getGlyphName(spell, world));
|
||||
|
||||
tooltip.add(spell.getTier().getDisplayNameWithFormatting());
|
||||
}
|
||||
/* Removed to streamline the tooltip a bit. Information is now within the book. if(spell.isContinuous){
|
||||
* tooltip.add("\u00A79Mana Cost: " + spell.cost + " per second"); }else{ tooltip.add("\u00A79Mana Cost: " +
|
||||
@@ -81,7 +72,7 @@ public class ItemSpellBook extends Item {
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public FontRenderer getFontRenderer(ItemStack stack){
|
||||
public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){
|
||||
return Wizardry.proxy.getFontRenderer(stack);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,34 +1,26 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import electroblob.wizardry.SpellGlyphData;
|
||||
import electroblob.wizardry.WizardData;
|
||||
import com.google.common.collect.Multimap;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.constants.Tier;
|
||||
import electroblob.wizardry.data.SpellGlyphData;
|
||||
import electroblob.wizardry.data.WizardData;
|
||||
import electroblob.wizardry.entity.living.ISummonedCreature;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.event.SpellCastEvent.Source;
|
||||
import electroblob.wizardry.packet.PacketCastSpell;
|
||||
import electroblob.wizardry.packet.WizardryPacketHandler;
|
||||
import electroblob.wizardry.registry.Spells;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryPotions;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import electroblob.wizardry.registry.*;
|
||||
import electroblob.wizardry.spell.Spell;
|
||||
import electroblob.wizardry.util.SpellModifiers;
|
||||
import electroblob.wizardry.util.WandHelper;
|
||||
import electroblob.wizardry.util.WizardryUtilities;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.entity.EntityPlayerSP;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.util.ITooltipFlag;
|
||||
import electroblob.wizardry.util.*;
|
||||
import electroblob.wizardry.util.ParticleBuilder.Type;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityLiving;
|
||||
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.inventory.Slot;
|
||||
@@ -38,46 +30,115 @@ import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ActionResult;
|
||||
import net.minecraft.util.EnumActionResult;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.util.SoundCategory;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.text.TextComponentTranslation;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
/**
|
||||
* This class is (literally) where the magic happens! All wand types are single instances of this class. There's a lot
|
||||
* of quite hard-to-read code in here, but unfortunately there's not much I can do about that. For this reason, I have
|
||||
* written the {@link WandHelper} class.<i> I strongly recommend you use it for interacting with wand items wherever
|
||||
* possible.</i>
|
||||
* <p>
|
||||
* It's unlikely that anything in this class will be of much use externally, but should you wish to use it for whatever
|
||||
* reason (perhaps if you extend it), it works as follows:
|
||||
* <p>
|
||||
* - onItemRightClick is where non-continuous spells are cast, and it sets the item in use for continuous spells<br>
|
||||
* - onUsingTick does the casting for continuous spells<br>
|
||||
* - onUpdate deals with the cooldowns for the spells
|
||||
*
|
||||
* This class is (literally) where the magic happens! All of wizardry's wand items are instances of this class. As of
|
||||
* wizardry 4.2, it is no longer necessary to extend {@code ItemWand} thanks to {@link ISpellCastingItem}, though
|
||||
* extending {@code ItemWand} may still be more appropriate for items using the same casting implementation.
|
||||
* <p></p>
|
||||
* This class handles spell casting as follows:
|
||||
* <p></p>
|
||||
* - {@code onItemRightClick} is where non-continuous spells are cast, and it sets the item in use for continuous spells<br>
|
||||
* - {@code onUsingTick} does the casting for continuous spells<br>
|
||||
* - {@code onUpdate} deals with the cooldowns for the spells<br>
|
||||
* <br>
|
||||
* See {@link ISpellCastingItem} for more detail on the {@code canCast(...)} and {@code cast(...)} methods.<br>
|
||||
* See {@link WandHelper} for everything related to wand NBT.
|
||||
*
|
||||
* @since Wizardry 1.0
|
||||
*/
|
||||
public class ItemWand extends Item implements IWorkbenchItem {
|
||||
@Mod.EventBusSubscriber
|
||||
public class ItemWand extends Item implements IWorkbenchItem, ISpellCastingItem, IManaStoringItem {
|
||||
|
||||
/** The number of spell slots a wand has with no attunement upgrades applied. */
|
||||
public static final int BASE_SPELL_SLOTS = 5;
|
||||
|
||||
/** The number of ticks between each time a continuous spell is added to the player's recently-cast spells. */
|
||||
private static final int CONTINUOUS_TRACKING_INTERVAL = 20;
|
||||
/** The increase in progression for casting spells of the matching element. */
|
||||
private static final float ELEMENTAL_PROGRESSION_MODIFIER = 1.2f;
|
||||
/** The fraction of progression lost when all recently-cast spells are the same as the one being cast. */
|
||||
private static final float MAX_PROGRESSION_REDUCTION = 0.75f;
|
||||
|
||||
public Tier tier;
|
||||
public Element element;
|
||||
|
||||
public ItemWand(Tier tier, Element element){
|
||||
super();
|
||||
setMaxStackSize(1);
|
||||
if(element == null || tier == Tier.BASIC){
|
||||
setCreativeTab(WizardryTabs.WIZARDRY);
|
||||
}
|
||||
setCreativeTab(WizardryTabs.GEAR);
|
||||
this.tier = tier;
|
||||
this.element = element;
|
||||
setMaxDamage(this.tier.maxCharge);
|
||||
WizardryRecipes.addToManaFlaskCharging(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spell getCurrentSpell(ItemStack stack){
|
||||
return WandHelper.getCurrentSpell(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spell[] getSpells(ItemStack stack){
|
||||
return WandHelper.getSpells(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectNextSpell(ItemStack stack){
|
||||
WandHelper.selectNextSpell(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectPreviousSpell(ItemStack stack){
|
||||
WandHelper.selectPreviousSpell(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showSpellHUD(EntityPlayer player, ItemStack stack){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showTooltip(ItemStack stack){
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Does nothing, use {@link ItemWand#setMana(ItemStack, int)} to modify wand mana. */
|
||||
@Override
|
||||
public void setDamage(ItemStack stack, int damage){
|
||||
// Overridden to do nothing to stop repair things from 'repairing' the mana in a wand
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMana(ItemStack stack, int mana){
|
||||
// Using super (which can only be done from in here) bypasses the above override
|
||||
super.setDamage(stack, getManaCapacity(stack) - mana);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMana(ItemStack stack){
|
||||
return getManaCapacity(stack) - getDamage(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getManaCapacity(ItemStack stack){
|
||||
return this.getMaxDamage(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -88,40 +149,111 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public FontRenderer getFontRenderer(ItemStack stack){
|
||||
public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){
|
||||
return Wizardry.proxy.getFontRenderer(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnchantable(ItemStack stack){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isBookEnchantable(ItemStack stack, ItemStack book){
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasEffect(ItemStack stack){
|
||||
return !Wizardry.settings.legacyWandLevelling && this.tier.level < Tier.MASTER.level
|
||||
&& WandHelper.getProgression(stack) >= Tier.values()[tier.ordinal() + 1].progression;
|
||||
}
|
||||
|
||||
// Max damage is modifiable with upgrades.
|
||||
@Override
|
||||
public int getMaxDamage(ItemStack itemstack){
|
||||
public int getMaxDamage(ItemStack stack){
|
||||
// + 0.5f corrects small float errors rounding down
|
||||
return (int)(super.getMaxDamage(itemstack) * (1.0f + Constants.STORAGE_INCREASE_PER_LEVEL
|
||||
* WandHelper.getUpgradeLevel(itemstack, WizardryItems.storage_upgrade)) + 0.5f);
|
||||
return (int)(super.getMaxDamage(stack) * (1.0f + Constants.STORAGE_INCREASE_PER_LEVEL
|
||||
* WandHelper.getUpgradeLevel(stack, WizardryItems.storage_upgrade)) + 0.5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpdate(ItemStack itemstack, World world, Entity entity, int slot, boolean isHeld){
|
||||
public void onCreated(ItemStack stack, World worldIn, EntityPlayer playerIn){
|
||||
setMana(stack, 0); // Wands are empty when first crafted
|
||||
}
|
||||
|
||||
WandHelper.decrementCooldowns(itemstack);
|
||||
@Override
|
||||
public void onUpdate(ItemStack stack, World world, Entity entity, int slot, boolean isHeld){
|
||||
|
||||
WandHelper.decrementCooldowns(stack);
|
||||
|
||||
// Decrements wand damage (increases mana) every 1.5 seconds if it has a condenser upgrade
|
||||
if(!world.isRemote && itemstack.isItemDamaged()
|
||||
&& world.getWorldTime() % Constants.CONDENSER_TICK_INTERVAL == 0){
|
||||
if(!world.isRemote && !this.isManaFull(stack) && world.getTotalWorldTime() % Constants.CONDENSER_TICK_INTERVAL == 0){
|
||||
// If the upgrade level is 0, this does nothing anyway.
|
||||
itemstack.setItemDamage(
|
||||
itemstack.getItemDamage() - WandHelper.getUpgradeLevel(itemstack, WizardryItems.condenser_upgrade));
|
||||
}
|
||||
|
||||
if(entity instanceof EntityPlayer && this.element != null && this.element != Element.MAGIC){
|
||||
// As it stands, this will trigger every tick. Not ideal, but I can't find a way to detect if a player
|
||||
// has a certain achievement.
|
||||
// TODO: check if this is somehow triggerable via JSON conditions.
|
||||
WizardryAdvancementTriggers.element_master.triggerFor((EntityPlayer)entity);
|
||||
this.rechargeMana(stack, WandHelper.getUpgradeLevel(stack, WizardryItems.condenser_upgrade));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Multimap<String, AttributeModifier> getAttributeModifiers(EntityEquipmentSlot slot, ItemStack stack){
|
||||
|
||||
Multimap<String, AttributeModifier> multimap = super.getAttributeModifiers(slot, stack);
|
||||
|
||||
if(slot == EntityEquipmentSlot.MAINHAND){
|
||||
int level = WandHelper.getUpgradeLevel(stack, WizardryItems.melee_upgrade);
|
||||
// This check doesn't affect the damage output, but it does stop a blank line from appearing in the tooltip.
|
||||
if(level > 0 && !this.isManaEmpty(stack)){
|
||||
multimap.put(SharedMonsterAttributes.ATTACK_DAMAGE.getName(),
|
||||
new AttributeModifier(ATTACK_DAMAGE_MODIFIER, "Melee upgrade modifier", 2 * level, 0));
|
||||
multimap.put(SharedMonsterAttributes.ATTACK_SPEED.getName(), new AttributeModifier(ATTACK_SPEED_MODIFIER, "Melee upgrade modifier", -2.4000000953674316D, 0));
|
||||
}
|
||||
}
|
||||
|
||||
return multimap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hitEntity(ItemStack stack, EntityLivingBase target, EntityLivingBase wielder){
|
||||
|
||||
int level = WandHelper.getUpgradeLevel(stack, WizardryItems.melee_upgrade);
|
||||
int mana = this.getMana(stack);
|
||||
|
||||
if(level > 0 && mana > 0) this.consumeMana(stack, level * 4, wielder);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canDestroyBlockInCreative(World world, BlockPos pos, ItemStack stack, EntityPlayer player){
|
||||
return WandHelper.getUpgradeLevel(stack, WizardryItems.melee_upgrade) == 0;
|
||||
}
|
||||
|
||||
// A proper hook was introduced for this in Forge build 14.23.5.2805 - Hallelujah, finally!
|
||||
// The discussion about this was quite interesting, see the following:
|
||||
// https://github.com/TeamTwilight/twilightforest/blob/1.12.x/src/main/java/twilightforest/item/ItemTFScepterLifeDrain.java
|
||||
// https://github.com/MinecraftForge/MinecraftForge/pull/4834
|
||||
// Among the things mentioned were that it can be 'fixed' by doing the exact same hacks that I did, and that
|
||||
// returning a result of PASS rather than SUCCESS from onItemRightClick also solves the problem (not sure why
|
||||
// though, and again it's not a perfect solution)
|
||||
// Edit: It seems that the hacky fix in previous versions actually introduced a wand duplication bug... oops
|
||||
|
||||
@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
|
||||
// Only called client-side
|
||||
// This method is always called on the item in oldStack, meaning that oldStack.getItem() == this
|
||||
public boolean shouldCauseReequipAnimation(ItemStack oldStack, ItemStack newStack, boolean slotChanged){
|
||||
|
||||
// This method does some VERY strange things! Despite its name, it also seems to affect the updating of NBT...
|
||||
@@ -149,18 +281,19 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
|
||||
@SideOnly(Side.CLIENT)
|
||||
@Override
|
||||
public void addInformation(ItemStack itemstack, World world, List<String> text, ITooltipFlag advanced){
|
||||
EntityPlayerSP player = Minecraft.getMinecraft().player;
|
||||
public void addInformation(ItemStack stack, World world, List<String> text, net.minecraft.client.util.ITooltipFlag advanced){
|
||||
|
||||
EntityPlayer player = net.minecraft.client.Minecraft.getMinecraft().player;
|
||||
if (player == null) { return; }
|
||||
// +0.5f is necessary due to the error in the way floats are calculated.
|
||||
if(element != null) text.add("\u00A78" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":wand.buff",
|
||||
(int)((tier.level + 1) * Constants.DAMAGE_INCREASE_PER_TIER * 100 + 0.5f) + "%",
|
||||
(int)((tier.level + 1) * Constants.POTENCY_INCREASE_PER_TIER * 100 + 0.5f) + "%",
|
||||
element.getDisplayName()));
|
||||
|
||||
Spell spell = WandHelper.getCurrentSpell(itemstack);
|
||||
Spell spell = WandHelper.getCurrentSpell(stack);
|
||||
|
||||
boolean discovered = true;
|
||||
if(Wizardry.settings.discoveryMode && !player.capabilities.isCreativeMode && WizardData.get(player) != null
|
||||
if(Wizardry.settings.discoveryMode && !player.isCreative() && WizardData.get(player) != null
|
||||
&& !WizardData.get(player).hasSpellBeenDiscovered(spell)){
|
||||
discovered = false;
|
||||
}
|
||||
@@ -169,8 +302,19 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
discovered ? "\u00A77" + spell.getDisplayNameWithFormatting()
|
||||
: "#\u00A79" + SpellGlyphData.getGlyphName(spell, player.world)));
|
||||
|
||||
text.add("\u00A79" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":wand.mana",
|
||||
(this.getMaxDamage(itemstack) - this.getDamage(itemstack)), this.getMaxDamage(itemstack)));
|
||||
if(advanced.isAdvanced()){
|
||||
// Advanced tooltips for debugging
|
||||
text.add("\u00A79" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":wand.mana",
|
||||
this.getMana(stack), this.getManaCapacity(stack)));
|
||||
|
||||
text.add("\u00A77" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":wand.progression",
|
||||
WandHelper.getProgression(stack), this.tier.level < Tier.MASTER.level ? Tier.values()[tier.ordinal() + 1].progression : 0));
|
||||
|
||||
// }else{
|
||||
//
|
||||
// ChargeStatus status = ChargeStatus.getChargeStatus(stack);
|
||||
// text.add(status.getFormattingCode() + status.getDisplayName());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -188,89 +332,29 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
ItemStack stack = player.getHeldItem(hand);
|
||||
|
||||
// Alternate right-click function; overrides spell casting.
|
||||
if(this.selectMinionTarget(player, world)) return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
if(this.selectMinionTarget(player, world)) return new ActionResult<>(EnumActionResult.SUCCESS, stack);
|
||||
|
||||
Spell spell = WandHelper.getCurrentSpell(stack);
|
||||
SpellModifiers modifiers = this.calculateModifiers(stack, spell);
|
||||
|
||||
// If anything stops the spell working at this point, nothing else happens.
|
||||
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Pre(player, spell, modifiers, Source.WAND))){
|
||||
return new ActionResult<ItemStack>(EnumActionResult.FAIL, stack);
|
||||
}
|
||||
|
||||
// This is here to start the inUse thing, otherwise the onUsingTick method will not fire.
|
||||
if(spell.isContinuous && !player.isHandActive()){
|
||||
player.setActiveHand(hand);
|
||||
// Probably ought to be here. (Does it succeed though?)
|
||||
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
}
|
||||
|
||||
// Conditions for the spell to be attempted. The tier check is a failsafe; it should never be false unless the
|
||||
// NBT is modified directly.
|
||||
if(!spell.isContinuous && spell.tier.level <= this.tier.level
|
||||
// Checks that the wand has enough mana to cast the spell
|
||||
&& spell.cost <= (stack.getMaxDamage() - stack.getItemDamage())
|
||||
// Checks that the spell is not in cooldown or that the player is in creative mode
|
||||
&& (WandHelper.getCurrentCooldown(stack) == 0 || player.capabilities.isCreativeMode)){
|
||||
|
||||
// If the spell does not require a packet, the code is run in the old client-inconsistent way, since this
|
||||
// means that swingItem() doesn't need packets in order to work, improving performance.
|
||||
if(!world.isRemote){
|
||||
|
||||
if(spell.cast(world, player, hand, 0, modifiers)){
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.WAND));
|
||||
|
||||
// = Packets =
|
||||
if(spell.doesSpellRequirePacket()){
|
||||
// Sends a packet to all players in dimension to tell them to spawn particles.
|
||||
// Only sent if the spell succeeded, because if the spell failed, you wouldn't
|
||||
// need to spawn any particles!
|
||||
IMessage msg = new PacketCastSpell.Message(player.getEntityId(), hand, spell.id(), modifiers);
|
||||
WizardryPacketHandler.net.sendToDimension(msg, world.provider.getDimension());
|
||||
}
|
||||
SpellModifiers modifiers = this.calculateModifiers(stack, player, spell);
|
||||
|
||||
if(canCast(stack, spell, player, hand, 0, modifiers)){
|
||||
// Now we can cast continuous spells with scrolls!
|
||||
if(spell.isContinuous){
|
||||
if(!player.isHandActive()){
|
||||
player.setActiveHand(hand);
|
||||
|
||||
// = Cooldown =
|
||||
// Spells only have a cooldown in survival
|
||||
if(!player.capabilities.isCreativeMode){
|
||||
|
||||
float cooldownMultiplier = 1.0f
|
||||
- WandHelper.getUpgradeLevel(stack, WizardryItems.cooldown_upgrade)
|
||||
* Constants.COOLDOWN_REDUCTION_PER_LEVEL;
|
||||
|
||||
if(player.isPotionActive(WizardryPotions.font_of_mana)){
|
||||
// Dividing by this rather than setting it takes upgrades and font of mana into account
|
||||
// simultaneously
|
||||
cooldownMultiplier /= 2
|
||||
+ player.getActivePotionEffect(WizardryPotions.font_of_mana).getAmplifier();
|
||||
}
|
||||
|
||||
WandHelper.setCurrentCooldown(stack, (int)(spell.cooldown * cooldownMultiplier));
|
||||
}
|
||||
|
||||
// = Mana cost =
|
||||
// The spell costs 20% less for every armour piece of the matching element.
|
||||
int armourPieces = getMatchingArmourCount(player, spell);
|
||||
|
||||
stack.damageItem((int)(spell.cost * (1.0f - armourPieces * Constants.COST_REDUCTION_PER_ARMOUR)),
|
||||
player);
|
||||
|
||||
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
// Store the modifiers for use each tick
|
||||
if(WizardData.get(player) != null) WizardData.get(player).itemCastingModifiers = modifiers;
|
||||
// Return the player's held item so spells can change it if they wish (e.g. possession)
|
||||
return new ActionResult<>(EnumActionResult.SUCCESS, player.getHeldItem(hand));
|
||||
}
|
||||
|
||||
}else if(!spell.doesSpellRequirePacket()){
|
||||
// Client-inconsistent spell casting. This code only runs client-side.
|
||||
if(spell.cast(world, player, hand, 0, modifiers)){
|
||||
// This is all that needs to happen, because everything above works fine on just the server side.
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.WAND));
|
||||
return new ActionResult<ItemStack>(EnumActionResult.SUCCESS, stack);
|
||||
}else{
|
||||
if(cast(stack, spell, player, hand, 0, modifiers)){
|
||||
return new ActionResult<>(EnumActionResult.SUCCESS, player.getHeldItem(hand));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new ActionResult<ItemStack>(EnumActionResult.FAIL, stack);
|
||||
return new ActionResult<>(EnumActionResult.FAIL, stack);
|
||||
}
|
||||
|
||||
// For continuous spells. The count argument actually decrements by 1 each tick.
|
||||
@@ -282,72 +366,155 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
EntityPlayer player = (EntityPlayer)user;
|
||||
|
||||
Spell spell = WandHelper.getCurrentSpell(stack);
|
||||
SpellModifiers modifiers = this.calculateModifiers(stack, spell);
|
||||
|
||||
SpellModifiers modifiers;
|
||||
|
||||
if(WizardData.get(player) != null){
|
||||
modifiers = WizardData.get(player).itemCastingModifiers;
|
||||
}else{
|
||||
modifiers = this.calculateModifiers(stack, (EntityPlayer)user, spell); // Fallback to the old way, should never be used
|
||||
}
|
||||
|
||||
int castingTick = stack.getMaxItemUseDuration() - count;
|
||||
|
||||
if(MinecraftForge.EVENT_BUS
|
||||
.post(new SpellCastEvent.Tick(player, spell, modifiers, Source.WAND, castingTick)))
|
||||
return;
|
||||
|
||||
// Continuous spells (these must check if they can be cast each tick since the mana changes)
|
||||
if(spell.isContinuous && spell.tier.level <= this.tier.level
|
||||
&& spell.cost / 5 <= (stack.getMaxDamage() - stack.getItemDamage())){
|
||||
// Don't call canCast when castingTick == 0 because we already did it in onItemRightClick
|
||||
if(spell.isContinuous && (castingTick == 0 || canCast(stack, spell, player, player.getActiveHand(), castingTick, modifiers))){
|
||||
cast(stack, spell, player, player.getActiveHand(), castingTick, modifiers);
|
||||
}else{
|
||||
// Stops the casting if it was interrupted, either by events or because the wand ran out of mana
|
||||
player.stopActiveHand();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(spell.cast(player.world, player, player.getActiveHand(), castingTick, modifiers)){
|
||||
@Override
|
||||
public boolean canCast(ItemStack stack, Spell spell, EntityPlayer caster, EnumHand hand, int castingTick, SpellModifiers modifiers){
|
||||
|
||||
if(castingTick == 0)
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.WAND));
|
||||
// Spells can only be cast if the casting events aren't cancelled...
|
||||
if(castingTick == 0){
|
||||
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Pre(Source.WAND, spell, caster, modifiers))) return false;
|
||||
}else{
|
||||
if(MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Tick(Source.WAND, spell, caster, modifiers, castingTick))) return false;
|
||||
}
|
||||
|
||||
// = Mana cost =
|
||||
// Divides the mana cost over a second appropriately; since damage is an integer it cannot
|
||||
// just be divided by 20.
|
||||
// Now does five times per second regardless of the spell cost, but each time it does 1/5 of the
|
||||
// cost per second.
|
||||
int tickNumber = (count % 20) + 1;
|
||||
// Tests if the tick counter is a multiple of 4 plus 1, i.e. is true when tickNumber = 1, 5, 9, 13
|
||||
// or 17.
|
||||
// Made a slight adjustment since the counter starts on 1 and not 4.
|
||||
if(tickNumber % 4 == 1){
|
||||
int cost = (int)(spell.getCost() * modifiers.get(SpellModifiers.COST));
|
||||
|
||||
int armourPieces = getMatchingArmourCount(player, spell);
|
||||
// As of wizardry 4.2 mana cost is only divided over two intervals each second
|
||||
if(spell.isContinuous) cost = getDistributedCost(cost, castingTick);
|
||||
|
||||
switch(armourPieces){
|
||||
// ...and the wand has enough mana to cast the spell...
|
||||
return cost <= this.getMana(stack) // This comes first because it changes over time
|
||||
// ...and the wand is the same tier as the spell or higher...
|
||||
&& spell.getTier().level <= this.tier.level
|
||||
// ...and either the spell is not in cooldown or the player is in creative mode
|
||||
&& (WandHelper.getCurrentCooldown(stack) == 0 || caster.isCreative());
|
||||
}
|
||||
|
||||
case 0:
|
||||
stack.damageItem(spell.cost / 5, player);
|
||||
break;
|
||||
@Override
|
||||
public boolean cast(ItemStack stack, Spell spell, EntityPlayer caster, EnumHand hand, int castingTick, SpellModifiers modifiers){
|
||||
|
||||
case 1:
|
||||
if(tickNumber != 17) stack.damageItem(spell.cost / 5, player);
|
||||
break;
|
||||
World world = caster.world;
|
||||
|
||||
case 2:
|
||||
if(tickNumber != 9 && tickNumber != 17) stack.damageItem(spell.cost / 5, player);
|
||||
break;
|
||||
if(world.isRemote && !spell.isContinuous && spell.requiresPacket()) return false;
|
||||
|
||||
case 3:
|
||||
if(tickNumber != 5 && tickNumber != 13 && tickNumber != 17)
|
||||
stack.damageItem(spell.cost / 5, player);
|
||||
break;
|
||||
if(spell.cast(world, caster, hand, castingTick, modifiers)){
|
||||
|
||||
case 4:
|
||||
if(tickNumber == 1) stack.damageItem(spell.cost / 5, player);
|
||||
break;
|
||||
if(castingTick == 0) MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(Source.WAND, spell, caster, modifiers));
|
||||
|
||||
}
|
||||
if(!world.isRemote){
|
||||
|
||||
// Continuous spells never require packets so don't rely on the requiresPacket method to specify it
|
||||
if(!spell.isContinuous && spell.requiresPacket()){
|
||||
// Sends a packet to all players in dimension to tell them to spawn particles.
|
||||
IMessage msg = new PacketCastSpell.Message(caster.getEntityId(), hand, spell, modifiers);
|
||||
WizardryPacketHandler.net.sendToDimension(msg, world.provider.getDimension());
|
||||
}
|
||||
|
||||
caster.setActiveHand(hand);
|
||||
|
||||
// Mana cost
|
||||
int cost = (int)(spell.getCost() * modifiers.get(SpellModifiers.COST));
|
||||
// As of wizardry 4.2 mana cost is only divided over two intervals each second
|
||||
if(spell.isContinuous) cost = getDistributedCost(cost, castingTick);
|
||||
|
||||
if(cost > 0) this.consumeMana(stack, cost, caster);
|
||||
|
||||
}
|
||||
|
||||
// Cooldown
|
||||
if(!spell.isContinuous && !caster.isCreative()){ // Spells only have a cooldown in survival
|
||||
WandHelper.setCurrentCooldown(stack, (int)(spell.getCooldown() * modifiers.get(WizardryItems.cooldown_upgrade)));
|
||||
}
|
||||
|
||||
// Progression
|
||||
if(this.tier.level < Tier.MASTER.level && castingTick % CONTINUOUS_TRACKING_INTERVAL == 0){
|
||||
|
||||
// We don't care about cost modifiers here, otherwise players would be penalised for wearing robes!
|
||||
int progression = (int)(spell.getCost() * modifiers.get(SpellModifiers.PROGRESSION));
|
||||
WandHelper.addProgression(stack, progression);
|
||||
|
||||
if(!Wizardry.settings.legacyWandLevelling){ // Don't display the message if legacy wand levelling is enabled
|
||||
// If the wand just gained enough progression to be upgraded...
|
||||
Tier nextTier = Tier.values()[tier.ordinal() + 1];
|
||||
int excess = WandHelper.getProgression(stack) - nextTier.progression;
|
||||
if(excess >= 0 && excess < progression){
|
||||
// ...display a message above the player's hotbar
|
||||
caster.playSound(WizardrySounds.ITEM_WAND_LEVELUP, 1.25f, 1);
|
||||
if(!world.isRemote)
|
||||
caster.sendMessage(new TextComponentTranslation("item." + Wizardry.MODID + ":wand.levelup",
|
||||
this.getItemStackDisplayName(stack), nextTier.getNameForTranslationFormatted()));
|
||||
}
|
||||
}
|
||||
|
||||
WizardData.get(caster).trackRecentSpell(spell);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPlayerStoppedUsing(ItemStack stack, World world, EntityLivingBase user, int timeLeft){
|
||||
|
||||
if(user instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)user;
|
||||
|
||||
Spell spell = WandHelper.getCurrentSpell(stack);
|
||||
|
||||
SpellModifiers modifiers;
|
||||
|
||||
if(WizardData.get(player) != null){
|
||||
modifiers = WizardData.get(player).itemCastingModifiers;
|
||||
}else{
|
||||
modifiers = this.calculateModifiers(stack, (EntityPlayer)user, spell); // Fallback to the old way, should never be used
|
||||
}
|
||||
|
||||
int castingTick = stack.getMaxItemUseDuration() - timeLeft; // Might as well include this
|
||||
|
||||
int cost = getDistributedCost((int)(spell.getCost() * modifiers.get(SpellModifiers.COST)), castingTick);
|
||||
|
||||
// Still need to check there's enough mana or the spell will finish twice, since running out of mana is
|
||||
// handled separately.
|
||||
if(spell.isContinuous && spell.getTier().level <= this.tier.level && cost <= this.getMana(stack)){
|
||||
|
||||
MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Finish(Source.WAND, spell, player, modifiers, castingTick));
|
||||
spell.finishCasting(world, player, Double.NaN, Double.NaN, Double.NaN, null, castingTick, modifiers);
|
||||
|
||||
if(!player.isCreative()){ // Spells only have a cooldown in survival
|
||||
WandHelper.setCurrentCooldown(stack, (int)(spell.getCooldown() * modifiers.get(WizardryItems.cooldown_upgrade)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean itemInteractionForEntity(ItemStack stack, EntityPlayer player, EntityLivingBase entity,
|
||||
EnumHand hand){
|
||||
public boolean itemInteractionForEntity(ItemStack stack, EntityPlayer player, EntityLivingBase entity, EnumHand hand){
|
||||
|
||||
if(player.isSneaking() && entity instanceof EntityPlayer && WizardData.get(player) != null){
|
||||
// This is one of those "the method doing the work looks as if it's just returning a value" situations.
|
||||
// ... I know, right?! I feel very programmer-y. But it's not too confusing here, and it looks neat.
|
||||
String string = WizardData.get(player).toggleAlly((EntityPlayer)entity) ? "item." + Wizardry.MODID + ":wand.addally"
|
||||
: "item." + Wizardry.MODID + ":wand.removeally";
|
||||
if(!player.world.isRemote) player.sendMessage(new TextComponentTranslation(string, entity.getName()));
|
||||
@@ -357,8 +524,27 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Distributes the given cost (which should be the per-second cost of a continuous spell) over a second and
|
||||
* returns the appropriate cost to be applied for the given tick. Currently the cost is distributed over 2
|
||||
* intervals per second, meaning the returned value is 0 unless {@code castingTick} is a multiple of 10.*/
|
||||
protected static int getDistributedCost(int cost, int castingTick){
|
||||
|
||||
int partialCost;
|
||||
|
||||
if(castingTick % 20 == 0){ // Whole number of seconds has elapsed
|
||||
partialCost = cost / 2 + cost % 2; // Make sure cost adds up to the correct value by adding the remainder here
|
||||
}else if(castingTick % 10 == 0){ // Something-and-a-half seconds has elapsed
|
||||
partialCost = cost/2;
|
||||
}else{ // Some other number of ticks has elapsed
|
||||
partialCost = 0; // Wands aren't damaged within half-seconds
|
||||
}
|
||||
|
||||
return partialCost;
|
||||
}
|
||||
|
||||
/** Returns a SpellModifiers object with the appropriate modifiers applied for the given ItemStack and Spell. */
|
||||
protected SpellModifiers calculateModifiers(ItemStack stack, Spell spell){
|
||||
// This is now public because artefacts use it
|
||||
public SpellModifiers calculateModifiers(ItemStack stack, EntityPlayer player, Spell spell){
|
||||
|
||||
SpellModifiers modifiers = new SpellModifiers();
|
||||
|
||||
@@ -375,38 +561,26 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
if(level > 0)
|
||||
modifiers.set(WizardryItems.blast_upgrade, 1.0f + level * Constants.BLAST_RADIUS_INCREASE_PER_LEVEL, true);
|
||||
|
||||
// I would have liked to have made potion effects increase in strength according to the damage multiplier,
|
||||
// but the amplifier level is too discrete to make this work. For example, wither 3 for 10 seconds will kill a
|
||||
// normal mob on full 20 health, but wither 2 for the same duration only deals about 6 hearts of damage in
|
||||
// total.
|
||||
if(this.element == spell.element){
|
||||
modifiers.set(SpellModifiers.POTENCY, 1.0f + (this.tier.level + 1) * Constants.DAMAGE_INCREASE_PER_TIER,
|
||||
true);
|
||||
level = WandHelper.getUpgradeLevel(stack, WizardryItems.cooldown_upgrade);
|
||||
if(level > 0)
|
||||
modifiers.set(WizardryItems.cooldown_upgrade, 1.0f - level * Constants.COOLDOWN_REDUCTION_PER_LEVEL, true);
|
||||
|
||||
float progressionModifier = 1.0f - ((float)WizardData.get(player).countRecentCasts(spell) / WizardData.MAX_RECENT_SPELLS)
|
||||
* MAX_PROGRESSION_REDUCTION;
|
||||
|
||||
if(this.element == spell.getElement()){
|
||||
modifiers.set(SpellModifiers.POTENCY, 1.0f + (this.tier.level + 1) * Constants.POTENCY_INCREASE_PER_TIER, true);
|
||||
progressionModifier *= ELEMENTAL_PROGRESSION_MODIFIER;
|
||||
}
|
||||
|
||||
modifiers.set(SpellModifiers.PROGRESSION, progressionModifier, false);
|
||||
|
||||
return modifiers;
|
||||
}
|
||||
|
||||
/** Counts the number of armour pieces the given player is wearing that match the given spell's element. */
|
||||
private int getMatchingArmourCount(EntityPlayer player, Spell spell){
|
||||
|
||||
int armourPieces = 0;
|
||||
|
||||
for(EntityEquipmentSlot slot : WizardryUtilities.ARMOUR_SLOTS){
|
||||
|
||||
ItemStack armour = player.getItemStackFromSlot(slot);
|
||||
|
||||
if(armour != null && armour.getItem() instanceof ItemWizardArmour
|
||||
&& ((ItemWizardArmour)armour.getItem()).element == spell.element)
|
||||
armourPieces++;
|
||||
}
|
||||
|
||||
return armourPieces;
|
||||
}
|
||||
|
||||
private boolean selectMinionTarget(EntityPlayer player, World world){
|
||||
|
||||
RayTraceResult rayTrace = WizardryUtilities.standardEntityRayTrace(world, player, 16, false);
|
||||
RayTraceResult rayTrace = RayTracer.standardEntityRayTrace(world, player, 16, false);
|
||||
|
||||
if(rayTrace != null && WizardryUtilities.isLiving(rayTrace.entityHit)){
|
||||
|
||||
@@ -430,6 +604,8 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Workbench stuff
|
||||
|
||||
@Override
|
||||
public int getSpellSlotCount(ItemStack stack){
|
||||
return BASE_SPELL_SLOTS + WandHelper.getUpgradeLevel(stack, WizardryItems.attunement_upgrade);
|
||||
@@ -444,24 +620,31 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
// and also the entire NBT tag compound.
|
||||
if(upgrade.getStack().getItem() == WizardryItems.arcane_tome){
|
||||
|
||||
// Checks the wand upgrade is for the tier above the wand's tier.
|
||||
Tier tier = Tier.values()[upgrade.getStack().getItemDamage()];
|
||||
|
||||
// Checks the wand upgrade is for the tier above the wand's tier, and that either the wand has enough
|
||||
// progression or the player is in creative mode.
|
||||
// It is guaranteed that: this == centre.getStack().getItem()
|
||||
if(upgrade.getStack().getItemDamage() - 1 == this.tier.ordinal()){
|
||||
|
||||
Tier tier = Tier.values()[upgrade.getStack().getItemDamage()];
|
||||
|
||||
ItemStack newWand = new ItemStack(WizardryUtilities.getWand(tier, this.element));
|
||||
if((player.isCreative() || Wizardry.settings.legacyWandLevelling
|
||||
|| WandHelper.getProgression(centre.getStack()) >= tier.progression)
|
||||
&& tier.ordinal() - 1 == this.tier.ordinal()){
|
||||
|
||||
// We're not carrying over excess progression for now, but if we do want to, this is how
|
||||
// if(!Wizardry.settings.legacyWandLevelling){
|
||||
// // Easy way to carry excess progression over to the new stack
|
||||
// WandHelper.setProgression(centre.getStack(), WandHelper.getProgression(centre.getStack()) - tier.progression);
|
||||
// }
|
||||
|
||||
ItemStack newWand = new ItemStack(WizardryItems.getWand(tier, this.element));
|
||||
newWand.setTagCompound(centre.getStack().getTagCompound());
|
||||
// This needs to be done after copying the tag compound so the max damage for the new wand
|
||||
// takes storage upgrades into account.
|
||||
newWand.setItemDamage(newWand.getMaxDamage() - (centre.getStack().getMaxDamage() - centre.getStack().getItemDamage()));
|
||||
|
||||
// This needs to be done after copying the tag compound so the mana capacity for the new wand
|
||||
// takes storage upgrades into account
|
||||
// Note the usage of the new wand item and not 'this' to ensure the correct capacity is used
|
||||
((IManaStoringItem)newWand.getItem()).setMana(newWand, this.getMana(centre.getStack()));
|
||||
|
||||
centre.putStack(newWand);
|
||||
upgrade.decrStackSize(1);
|
||||
|
||||
if(tier == Tier.APPRENTICE) WizardryAdvancementTriggers.apprentice.triggerFor(player);
|
||||
if(tier == Tier.MASTER) WizardryAdvancementTriggers.master.triggerFor(player);
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
@@ -474,14 +657,14 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
&& WandHelper.getUpgradeLevel(centre.getStack(), specialUpgrade) < Constants.UPGRADE_STACK_LIMIT){
|
||||
|
||||
// Used to preserve existing mana when upgrading storage rather than creating free mana.
|
||||
int prevMana = centre.getStack().getMaxDamage() - centre.getStack().getItemDamage();
|
||||
int prevMana = this.getMana(centre.getStack());
|
||||
|
||||
WandHelper.applyUpgrade(centre.getStack(), specialUpgrade);
|
||||
|
||||
// Special behaviours for specific upgrades
|
||||
if(specialUpgrade == WizardryItems.storage_upgrade){
|
||||
|
||||
centre.getStack().setItemDamage(centre.getStack().getMaxDamage() - prevMana);
|
||||
|
||||
this.setMana(centre.getStack(), prevMana);
|
||||
|
||||
}else if(specialUpgrade == WizardryItems.attunement_upgrade){
|
||||
|
||||
@@ -501,9 +684,7 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
int[] newCooldowns = new int[newSlotCount];
|
||||
|
||||
if(cooldowns.length > 0){
|
||||
for(int i = 0; i < cooldowns.length; i++){
|
||||
newCooldowns[i] = cooldowns[i];
|
||||
}
|
||||
System.arraycopy(cooldowns, 0, newCooldowns, 0, cooldowns.length);
|
||||
}
|
||||
|
||||
WandHelper.setCooldowns(centre.getStack(), newCooldowns);
|
||||
@@ -520,7 +701,7 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
}
|
||||
}
|
||||
|
||||
// Reads NBT spell id array to variable, edits this, then writes it back to NBT.
|
||||
// Reads NBT spell metadata array to variable, edits this, then writes it back to NBT.
|
||||
// Original spells are preserved; if a slot is left empty the existing spell binding will remain.
|
||||
// Accounts for spells which cannot be applied because they are above the wand's tier; these spells
|
||||
// will not bind but the existing spell in that slot will remain and other applicable spells will
|
||||
@@ -535,9 +716,9 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
for(int i = 0; i < spells.length; i++){
|
||||
if(spellBooks[i].getStack() != ItemStack.EMPTY){
|
||||
|
||||
Spell spell = Spell.get(spellBooks[i].getStack().getItemDamage());
|
||||
// If the wand is powerful enough for the spell and it's not already bound to that slot
|
||||
if(!(spell.tier.level > this.tier.level) && spells[i] != spell){
|
||||
Spell spell = Spell.byMetadata(spellBooks[i].getStack().getItemDamage());
|
||||
// If the wand is powerful enough for the spell, it's not already bound to that slot and it's enabled for wands
|
||||
if(!(spell.getTier().level > this.tier.level) && spells[i] != spell && spell.isEnabled(SpellProperties.Context.WANDS)){
|
||||
spells[i] = spell;
|
||||
changed = true;
|
||||
}
|
||||
@@ -547,24 +728,67 @@ public class ItemWand extends Item implements IWorkbenchItem {
|
||||
WandHelper.setSpells(centre.getStack(), spells);
|
||||
|
||||
// Charges wand by appropriate amount
|
||||
if(crystals.getStack() != ItemStack.EMPTY){
|
||||
if(crystals.getStack() != ItemStack.EMPTY && !this.isManaFull(centre.getStack())){
|
||||
|
||||
int chargeDepleted = centre.getStack().getItemDamage();
|
||||
|
||||
if(crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL < chargeDepleted){
|
||||
|
||||
centre.getStack().setItemDamage(chargeDepleted - crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL);
|
||||
crystals.decrStackSize(crystals.getStack().getCount());
|
||||
changed = true;
|
||||
|
||||
}else if(chargeDepleted != 0){
|
||||
int chargeDepleted = this.getManaCapacity(centre.getStack()) - this.getMana(centre.getStack());
|
||||
|
||||
centre.getStack().setItemDamage(0);
|
||||
crystals.decrStackSize((int)Math.ceil(((double)chargeDepleted) / Constants.MANA_PER_CRYSTAL));
|
||||
changed = true;
|
||||
int manaPerItem = Constants.MANA_PER_CRYSTAL;
|
||||
if(crystals.getStack().getItem() == WizardryItems.crystal_shard) manaPerItem = Constants.MANA_PER_SHARD;
|
||||
if(crystals.getStack().getItem() == WizardryItems.grand_crystal) manaPerItem = Constants.GRAND_CRYSTAL_MANA;
|
||||
|
||||
if(crystals.getStack().getCount() * manaPerItem < chargeDepleted){
|
||||
// If there aren't enough crystals to fully charge the wand
|
||||
this.rechargeMana(centre.getStack(), crystals.getStack().getCount() * manaPerItem);
|
||||
crystals.decrStackSize(crystals.getStack().getCount());
|
||||
|
||||
}else{
|
||||
// If there are excess crystals (or just enough)
|
||||
this.setMana(centre.getStack(), this.getManaCapacity(centre.getStack()));
|
||||
crystals.decrStackSize((int)Math.ceil(((double)chargeDepleted) / manaPerItem));
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
// hitEntity is only called server-side, so we'll have to use events
|
||||
@SubscribeEvent
|
||||
public static void onAttackEntityEvent(AttackEntityEvent event){
|
||||
|
||||
EntityPlayer player = event.getEntityPlayer();
|
||||
ItemStack stack = player.getHeldItemMainhand(); // Can't melee with offhand items
|
||||
|
||||
if(stack.getItem() instanceof IManaStoringItem){
|
||||
|
||||
// Nobody said it had to be a wand, as long as it's got a melee upgrade it counts
|
||||
int level = WandHelper.getUpgradeLevel(stack, WizardryItems.melee_upgrade);
|
||||
int mana = ((IManaStoringItem)stack.getItem()).getMana(stack);
|
||||
|
||||
if(level > 0 && mana > 0){
|
||||
|
||||
Random random = player.world.rand;
|
||||
|
||||
player.world.playSound(player.posX, player.posY, player.posZ, WizardrySounds.ITEM_WAND_MELEE, SoundCategory.PLAYERS, 0.75f, 1, false);
|
||||
|
||||
if(player.world.isRemote){
|
||||
|
||||
Vec3d origin = new Vec3d(player.posX, player.getEntityBoundingBox().minY + player.getEyeHeight(), player.posZ);
|
||||
Vec3d hit = origin.add(player.getLookVec().scale(player.getDistance(event.getTarget())));
|
||||
// Generate two perpendicular vectors in the plane perpendicular to the look vec
|
||||
Vec3d vec1 = player.getLookVec().rotatePitch(90);
|
||||
Vec3d vec2 = player.getLookVec().crossProduct(vec1);
|
||||
|
||||
for(int i = 0; i < 15; i++){
|
||||
ParticleBuilder.create(Type.SPARKLE).pos(hit)
|
||||
.vel(vec1.scale(random.nextFloat() * 0.3f - 0.15f).add(vec2.scale(random.nextFloat() * 0.3f - 0.15f)))
|
||||
.clr(1f, 1f, 1f).fade(0.3f, 0.5f, 1)
|
||||
.time(8 + random.nextInt(4)).spawn(player.world);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import net.minecraft.item.EnumRarity;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public class ItemWandUpgrade extends Item {
|
||||
|
||||
public ItemWandUpgrade(){
|
||||
super();
|
||||
this.setCreativeTab(WizardryTabs.WIZARDRY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EnumRarity getRarity(ItemStack stack){
|
||||
return EnumRarity.UNCOMMON;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void addInformation(ItemStack stack, @Nullable World world, List<String> tooltip, net.minecraft.client.util.ITooltipFlag flag) {
|
||||
Wizardry.proxy.addMultiLineDescription(tooltip, "item." + this.getRegistryName() + ".desc");
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,24 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.common.collect.HashMultimap;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.block.BlockStatue;
|
||||
import electroblob.wizardry.constants.Constants;
|
||||
import electroblob.wizardry.constants.Element;
|
||||
import electroblob.wizardry.event.SpellCastEvent;
|
||||
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
|
||||
import electroblob.wizardry.registry.WizardryItems;
|
||||
import electroblob.wizardry.registry.WizardryRecipes;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import net.minecraft.client.model.ModelBiped;
|
||||
import net.minecraft.client.util.ITooltipFlag;
|
||||
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.SharedMonsterAttributes;
|
||||
import net.minecraft.entity.ai.attributes.AttributeModifier;
|
||||
import net.minecraft.entity.ai.attributes.IAttributeInstance;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.inventory.EntityEquipmentSlot;
|
||||
import net.minecraft.inventory.Slot;
|
||||
@@ -28,14 +28,19 @@ import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.util.EnumHandSide;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
|
||||
import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.eventhandler.EventPriority;
|
||||
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
|
||||
import net.minecraftforge.fml.relauncher.Side;
|
||||
import net.minecraftforge.fml.relauncher.SideOnly;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Mod.EventBusSubscriber
|
||||
public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem {
|
||||
public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem, IManaStoringItem {
|
||||
|
||||
// VanillaCopy, ItemArmor has this set to private for some reason.
|
||||
public static final UUID[] ARMOR_MODIFIERS = new UUID[] {UUID.fromString("845DB27C-C624-495F-8C9F-6020A9A58B6B"), UUID.fromString("D8499B04-0E66-4726-AB29-64469D734E0D"), UUID.fromString("9F3D476D-C118-4544-8365-64846904B48E"), UUID.fromString("2AD3F246-FEE1-4E67-B886-69FD380BB150")};
|
||||
@@ -47,20 +52,53 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem {
|
||||
public ItemWizardArmour(ArmorMaterial material, int renderIndex, EntityEquipmentSlot armourType, Element element){
|
||||
super(material, renderIndex, armourType);
|
||||
this.element = element;
|
||||
setCreativeTab(WizardryTabs.WIZARDRY);
|
||||
setCreativeTab(WizardryTabs.GEAR);
|
||||
WizardryRecipes.addToManaFlaskCharging(this);
|
||||
}
|
||||
|
||||
/** Should only be used by vanilla's armour damage calculations; use {@link ItemWizardArmour#setMana(ItemStack, int)}
|
||||
* to modify wand mana from elsewhere. */
|
||||
@Override
|
||||
public void setDamage(ItemStack stack, int damage){
|
||||
// Overridden to stop repair things from 'repairing' the mana in wizard armour
|
||||
// This being armour, it's much easier to let its damage increase normally, but block it from being decreased
|
||||
if(stack.getItemDamage() < damage) super.setDamage(stack, damage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMana(ItemStack stack, int mana){
|
||||
// Using super (which can only be done from in here) bypasses the above override
|
||||
super.setDamage(stack, getManaCapacity(stack) - mana);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMana(ItemStack stack){
|
||||
return getManaCapacity(stack) - getDamage(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getManaCapacity(ItemStack stack){
|
||||
return this.getMaxDamage(stack);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public void addInformation(ItemStack stack, World world, List<String> tooltip, ITooltipFlag advanced){
|
||||
public void addInformation(ItemStack stack, World world, List<String> tooltip, net.minecraft.client.util.ITooltipFlag advanced){
|
||||
|
||||
if(stack.hasTagCompound() && stack.getTagCompound().getBoolean("legendary")) tooltip
|
||||
.add("\u00A7d" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":wizard_armour.legendary"));
|
||||
if(element != null)
|
||||
if(stack.hasTagCompound() && stack.getTagCompound().getBoolean("legendary"))
|
||||
tooltip.add("\u00A7d" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":wizard_armour.legendary"));
|
||||
|
||||
if(element != null){
|
||||
tooltip.add("\u00A78" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":wizard_armour.buff",
|
||||
(int)(Constants.COST_REDUCTION_PER_ARMOUR * 100) + "%", element.getDisplayName()));
|
||||
tooltip.add("\u00A79" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":wizard_armour.mana",
|
||||
(this.getMaxDamage(stack) - this.getDamage(stack)), this.getMaxDamage(stack)));
|
||||
}
|
||||
|
||||
// tooltip.add("\u00A79" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":wizard_armour.mana",
|
||||
// (this.getMaxDamage(stack) - this.getDamage(stack)), this.getMaxDamage(stack)));
|
||||
|
||||
// ChargeStatus status = ChargeStatus.getChargeStatus(stack);
|
||||
//
|
||||
// tooltip.add(status.getFormattingCode() + status.getDisplayName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,32 +108,23 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem {
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public boolean hasEffect(ItemStack stack){
|
||||
return stack.hasTagCompound() && stack.getTagCompound().getBoolean("legendary");
|
||||
}
|
||||
|
||||
@Override
|
||||
@SideOnly(Side.CLIENT)
|
||||
public ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack,
|
||||
EntityEquipmentSlot armourSlot, ModelBiped _default){
|
||||
|
||||
ModelBiped model = Wizardry.proxy.getWizardArmourModel();
|
||||
public net.minecraft.client.model.ModelBiped getArmorModel(EntityLivingBase entityLiving, ItemStack itemStack,
|
||||
EntityEquipmentSlot armourSlot, net.minecraft.client.model.ModelBiped _default){
|
||||
|
||||
// Legs use modelBiped
|
||||
if(armourSlot == EntityEquipmentSlot.LEGS) return null;
|
||||
if(armourSlot == EntityEquipmentSlot.LEGS && !entityLiving.isInvisible()) return null;
|
||||
|
||||
net.minecraft.client.model.ModelBiped model = Wizardry.proxy.getWizardArmourModel();
|
||||
|
||||
if(model != null){
|
||||
|
||||
model.bipedHead.showModel = armourSlot == EntityEquipmentSlot.HEAD;
|
||||
model.bipedHeadwear.showModel = false;
|
||||
model.bipedBody.showModel = armourSlot == EntityEquipmentSlot.CHEST
|
||||
|| armourSlot == EntityEquipmentSlot.LEGS;
|
||||
model.bipedBody.showModel = armourSlot == EntityEquipmentSlot.CHEST;
|
||||
model.bipedRightArm.showModel = armourSlot == EntityEquipmentSlot.CHEST;
|
||||
model.bipedLeftArm.showModel = armourSlot == EntityEquipmentSlot.CHEST;
|
||||
model.bipedRightLeg.showModel = armourSlot == EntityEquipmentSlot.LEGS
|
||||
|| armourSlot == EntityEquipmentSlot.FEET;
|
||||
model.bipedLeftLeg.showModel = armourSlot == EntityEquipmentSlot.LEGS
|
||||
|| armourSlot == EntityEquipmentSlot.FEET;
|
||||
model.bipedRightLeg.showModel = armourSlot == EntityEquipmentSlot.FEET;
|
||||
model.bipedLeftLeg.showModel = armourSlot == EntityEquipmentSlot.FEET;
|
||||
|
||||
model.isSneak = entityLiving.isSneaking();
|
||||
model.isRiding = entityLiving.isRiding();
|
||||
@@ -107,29 +136,29 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem {
|
||||
ItemStack itemstackL = leftHanded ? entityLiving.getHeldItemMainhand() : entityLiving.getHeldItemOffhand();
|
||||
|
||||
if(!itemstackR.isEmpty()){
|
||||
model.rightArmPose = ModelBiped.ArmPose.ITEM;
|
||||
model.rightArmPose = net.minecraft.client.model.ModelBiped.ArmPose.ITEM;
|
||||
|
||||
if(entityLiving.getItemInUseCount() > 0){
|
||||
EnumAction enumaction = itemstackR.getItemUseAction();
|
||||
|
||||
if(enumaction == EnumAction.BLOCK){
|
||||
model.rightArmPose = ModelBiped.ArmPose.BLOCK;
|
||||
model.rightArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BLOCK;
|
||||
}else if(enumaction == EnumAction.BOW){
|
||||
model.rightArmPose = ModelBiped.ArmPose.BOW_AND_ARROW;
|
||||
model.rightArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BOW_AND_ARROW;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!itemstackL.isEmpty()){
|
||||
model.leftArmPose = ModelBiped.ArmPose.ITEM;
|
||||
model.leftArmPose = net.minecraft.client.model.ModelBiped.ArmPose.ITEM;
|
||||
|
||||
if(entityLiving.getItemInUseCount() > 0){
|
||||
EnumAction enumaction1 = itemstackL.getItemUseAction();
|
||||
|
||||
if(enumaction1 == EnumAction.BLOCK){
|
||||
model.leftArmPose = ModelBiped.ArmPose.BLOCK;
|
||||
model.leftArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BLOCK;
|
||||
}else if(enumaction1 == EnumAction.BOW){
|
||||
model.leftArmPose = ModelBiped.ArmPose.BOW_AND_ARROW;
|
||||
model.leftArmPose = net.minecraft.client.model.ModelBiped.ArmPose.BOW_AND_ARROW;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,33 +173,33 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem {
|
||||
// Returns a completely transparent texture if the player is invisible. This is such an annoyingly easy
|
||||
// fix, considering how long I spent trying to do this before - a bit of lateral thinking was all it took.
|
||||
// Do note however that a texture pack could override this.
|
||||
if(entity instanceof EntityLivingBase && ((EntityLivingBase)entity).isInvisible()
|
||||
&& !entity.getEntityData().getBoolean(BlockStatue.NBT_KEY))
|
||||
return "ebwizardry:textures/armour/invisible_armour.png";
|
||||
// if(entity instanceof EntityLivingBase && entity.isInvisible() && !entity.getEntityData().getBoolean(BlockStatue.PETRIFIED_NBT_KEY))
|
||||
// return "ebwizardry:textures/armour/invisible_armour.png";
|
||||
|
||||
if(slot == EntityEquipmentSlot.LEGS)
|
||||
return this.element == null ? "ebwizardry:textures/armour/wizard_armour_legs.png"
|
||||
: "ebwizardry:textures/armour/wizard_armour_" + this.element.getUnlocalisedName() + "_legs.png";
|
||||
String s = "wizard_armour";
|
||||
|
||||
return this.element == null ? "ebwizardry:textures/armour/wizard_armour.png"
|
||||
: "ebwizardry:textures/armour/wizard_armour_" + this.element.getUnlocalisedName() + ".png";
|
||||
if(this.element != null) s = s + "_" + this.element.getName();
|
||||
if(slot == EntityEquipmentSlot.LEGS) s = s + "_legs";
|
||||
if(stack.hasTagCompound() && stack.getTagCompound().getBoolean("legendary")) s = "legendary_" + s;
|
||||
|
||||
return "ebwizardry:textures/armour/" + s + ".png";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getIsRepairable(ItemStack stack, ItemStack par2ItemStack){
|
||||
public boolean getIsRepairable(ItemStack stack, ItemStack material){
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Properly handles the defense value of the armor. This method is responisble for the tooltip on top of
|
||||
* the armor value. It is also what handles armor toughness, but the wizard armor had a value of 0 for that.
|
||||
/**
|
||||
* Properly handles the defence value of the armour. This method is responsible for the tooltip on top of
|
||||
* the armour value. It is also what handles armour toughness.
|
||||
*/
|
||||
@Override
|
||||
public Multimap<String, AttributeModifier> getAttributeModifiers(EntityEquipmentSlot slot, ItemStack stack){
|
||||
|
||||
Multimap<String, AttributeModifier> map = HashMultimap.create();
|
||||
|
||||
if(stack.getItemDamage() < stack.getMaxDamage() && this.armorType == slot){
|
||||
if(!this.isManaEmpty(stack) && this.armorType == slot){
|
||||
|
||||
int defense = reductions[slot.getIndex()];
|
||||
float toughness = 0f;
|
||||
@@ -189,30 +218,10 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem {
|
||||
return map;
|
||||
}
|
||||
|
||||
// Fixes wizard armor breaking by disallowing setting damage above the max, since damageArmor is not always called.
|
||||
// Since ISpecialArmor has been removed from this class, this may no longer be necessary, but keeping it won't hurt.
|
||||
// Workbench stuff
|
||||
|
||||
@Override
|
||||
public void setDamage(ItemStack stack, int damage) {
|
||||
if(damage <= stack.getMaxDamage()) super.setDamage(stack, damage);
|
||||
else super.setDamage(stack, stack.getMaxDamage());
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
|
||||
if(event.getEntityLiving() instanceof EntityPlayer){
|
||||
|
||||
EntityPlayer player = (EntityPlayer)event.getEntityLiving();
|
||||
|
||||
for(ItemStack stack : player.getArmorInventoryList()){
|
||||
if(!(stack.getItem() instanceof ItemWizardArmour)){
|
||||
return; // If any of the armour slots doesn't contain wizard armour, don't trigger the achievement.
|
||||
}
|
||||
}
|
||||
// If it gets this far, then all slots must be wizard armour, so trigger the achievement.
|
||||
WizardryAdvancementTriggers.armour_set.triggerFor(player);
|
||||
}
|
||||
}
|
||||
public boolean showTooltip(ItemStack stack){ return true; }
|
||||
|
||||
@Override
|
||||
public int getSpellSlotCount(ItemStack stack){
|
||||
@@ -241,25 +250,87 @@ public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem {
|
||||
}
|
||||
|
||||
// Charges armour by appropriate amount
|
||||
if(crystals.getStack() != ItemStack.EMPTY){
|
||||
|
||||
int chargeDepleted = centre.getStack().getItemDamage();
|
||||
|
||||
if(crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL < chargeDepleted){
|
||||
|
||||
centre.getStack().setItemDamage(chargeDepleted - crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL);
|
||||
crystals.decrStackSize(crystals.getStack().getCount());
|
||||
changed = true;
|
||||
|
||||
}else if(chargeDepleted != 0){
|
||||
if(crystals.getStack() != ItemStack.EMPTY && !this.isManaFull(centre.getStack())){
|
||||
|
||||
centre.getStack().setItemDamage(0);
|
||||
int chargeDepleted = this.getManaCapacity(centre.getStack()) - this.getMana(centre.getStack());
|
||||
|
||||
if(crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL < chargeDepleted){
|
||||
// If there aren't enough crystals to fully charge the armour
|
||||
this.rechargeMana(centre.getStack(), crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL);
|
||||
crystals.decrStackSize(crystals.getStack().getCount());
|
||||
|
||||
}else{
|
||||
// If there are excess crystals (or just enough)
|
||||
this.setMana(centre.getStack(), this.getManaCapacity(centre.getStack()));
|
||||
crystals.decrStackSize((int)Math.ceil(((double)chargeDepleted) / Constants.MANA_PER_CRYSTAL));
|
||||
changed = true;
|
||||
}
|
||||
|
||||
changed = true;
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
|
||||
// Event Handlers
|
||||
|
||||
// @SubscribeEvent
|
||||
// public static void onLivingUpdateEvent(LivingUpdateEvent event){
|
||||
//
|
||||
// if(event.getEntityLiving() instanceof EntityPlayer){
|
||||
//
|
||||
// EntityPlayer player = (EntityPlayer)event.getEntityLiving();
|
||||
//
|
||||
// for(ItemStack stack : player.getArmorInventoryList()){
|
||||
// if(!(stack.getItem() instanceof ItemWizardArmour)){
|
||||
// return; // If any of the armour slots doesn't contain wizard armour, don't trigger the achievement.
|
||||
// }
|
||||
// }
|
||||
// // If it gets this far, then all slots must be wizard armour, so trigger the achievement.
|
||||
// WizardryAdvancementTriggers.armour_set.triggerFor(player);
|
||||
// }
|
||||
// }
|
||||
|
||||
@SubscribeEvent(priority = EventPriority.LOW)
|
||||
public static void onSpellCastPreEvent(SpellCastEvent.Pre event){
|
||||
// Armour cost reduction
|
||||
if(event.getCaster() == null) return;
|
||||
int armourPieces = getMatchingArmourCount(event.getCaster(), event.getSpell().getElement());
|
||||
float multiplier = 1f - armourPieces * Constants.COST_REDUCTION_PER_ARMOUR;
|
||||
if(armourPieces == WizardryUtilities.ARMOUR_SLOTS.length) multiplier -= Constants.FULL_ARMOUR_SET_BONUS;
|
||||
event.getModifiers().set(SpellModifiers.COST, event.getModifiers().get(SpellModifiers.COST) * multiplier, false);
|
||||
}
|
||||
|
||||
/** Counts the number of armour pieces the given entity is wearing that match the given element. */
|
||||
public static int getMatchingArmourCount(EntityLivingBase entity, Element element){
|
||||
return (int)Arrays.stream(WizardryUtilities.ARMOUR_SLOTS)
|
||||
.map(s -> entity.getItemStackFromSlot(s).getItem())
|
||||
.filter(i -> i instanceof ItemWizardArmour && ((ItemWizardArmour)i).element == element)
|
||||
.count();
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){
|
||||
// Undo the mob detection penalty for wearing armour when invisible
|
||||
// Only bother doing this for players because the penalty only applies to them
|
||||
if(event.getTarget() instanceof EntityPlayer && event.getEntityLiving() instanceof EntityLiving
|
||||
&& event.getEntityLiving().isInvisible()){
|
||||
|
||||
int armourPieces = (int)Streams.stream(event.getTarget().getArmorInventoryList())
|
||||
.filter(s -> !s.isEmpty() && !(s.getItem() instanceof ItemWizardArmour))
|
||||
.count();
|
||||
|
||||
if(armourPieces == 0) return;
|
||||
|
||||
// Repeat the calculation from EntityAIFindNearestPlayer, but ignoring wizard armour
|
||||
IAttributeInstance attribute = event.getEntityLiving().getEntityAttribute(SharedMonsterAttributes.FOLLOW_RANGE);
|
||||
double followRange = attribute == null ? 16 : attribute.getAttributeValue();
|
||||
if(event.getTarget().isSneaking()) followRange *= 0.8;
|
||||
float f = armourPieces / ((EntityPlayer)event.getTarget()).inventory.armorInventory.size();
|
||||
if(f < 0.1F) f = 0.1F;
|
||||
followRange *= (double)(0.7F * f);
|
||||
// Don't need to worry about the isSuitableTarget check since it must already have been checked to get this far
|
||||
if(event.getTarget().getDistance(event.getEntity()) > followRange) ((EntityLiving)event.getEntityLiving()).setAttackTarget(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
package electroblob.wizardry.item;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import electroblob.wizardry.Wizardry;
|
||||
import electroblob.wizardry.WizardryGuiHandler;
|
||||
import electroblob.wizardry.registry.WizardryTabs;
|
||||
import net.minecraft.client.util.ITooltipFlag;
|
||||
import net.minecraft.entity.player.EntityPlayer;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
@@ -16,6 +11,9 @@ import net.minecraft.util.EnumActionResult;
|
||||
import net.minecraft.util.EnumHand;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
|
||||
public class ItemWizardHandbook extends Item {
|
||||
|
||||
// Yep, I hardcoded my own name into the mod. Don't want people changing it now, do I?
|
||||
@@ -28,7 +26,7 @@ public class ItemWizardHandbook extends Item {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInformation(ItemStack stack, @Nullable World worldIn, List<String> tooltip, ITooltipFlag flagIn) {
|
||||
public void addInformation(ItemStack stack, @Nullable World world, List<String> tooltip, net.minecraft.client.util.ITooltipFlag flag) {
|
||||
tooltip.add(
|
||||
"\u00A77" + net.minecraft.client.resources.I18n.format("item." + Wizardry.MODID + ":wizard_handbook.desc", AUTHOR));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user