wizards = WizardryUtilities.getEntitiesWithinRadius(64, event.getPos().getX(),
+ event.getPos().getY(), event.getPos().getZ(), event.getWorld(), EntityWizard.class);
+
+ if(!wizards.isEmpty()){
+ for(EntityWizard wizard : wizards){
+ if(wizard.isBlockPartOfTower(event.getPos())){
+ wizard.setRevengeTarget(event.getPlayer());
+ event.getPlayer().addStat(WizardryAchievements.anger_wizard);
+ }
+ }
+ }
+ }
+ }
// EntityVillager overrides (that don't add features)
diff --git a/src/main/java/electroblob/wizardry/entity/living/ISummonedCreature.java b/src/main/java/electroblob/wizardry/entity/living/ISummonedCreature.java
index cc5a3a35..39c68a28 100644
--- a/src/main/java/electroblob/wizardry/entity/living/ISummonedCreature.java
+++ b/src/main/java/electroblob/wizardry/entity/living/ISummonedCreature.java
@@ -13,6 +13,10 @@ import electroblob.wizardry.WizardData;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.WizardryEventHandler;
import electroblob.wizardry.item.ItemWand;
+import electroblob.wizardry.util.IElementalDamage;
+import electroblob.wizardry.util.IndirectMinionDamage;
+import electroblob.wizardry.util.MagicDamage.DamageType;
+import electroblob.wizardry.util.MinionDamage;
import electroblob.wizardry.util.WizardryParticleType;
import electroblob.wizardry.util.WizardryUtilities;
import io.netty.buffer.ByteBuf;
@@ -23,10 +27,16 @@ import net.minecraft.entity.monster.IMob;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.DamageSource;
+import net.minecraft.util.EntityDamageSource;
+import net.minecraft.util.EntityDamageSourceIndirect;
import net.minecraft.util.EnumHand;
+import net.minecraftforge.event.entity.living.LivingAttackEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
-/** Interface for all summoned creatures. The code for summoned creatures has been overhauled in Wizardry 1.2, and this
+/** Interface for all summoned creatures. The code for summoned creatures has been overhauled in Wizardry 2.1, and this
* interface allows summoned creatures to extend vanilla (or indeed modded) entity classes, so EntitySummonedZombie
* now extends EntityZombie, for example. This change has two major benefits:
*
@@ -59,13 +69,14 @@ import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
*
* Due to the limitations of interfaces, some methods that really ought to be protected are public. These are clearly
* marked as 'Internal, DO NOT CALL'. Don't call them, only implement them.
- * @since Wizardry 1.2
+ * @since Wizardry 2.1
* @author Electroblob */
/*
* Quite honestly, this is not what default methods are really for. However, this is modding, and in modding some
* sacrifices have to be made when it comes to Java style - because adding on to a pre-existing program is not a
* good way of doing this sort of thing anyway, but we have no choice about that!
*/
+@Mod.EventBusSubscriber
public interface ISummonedCreature extends IEntityAdditionalSpawnData {
// Remember that ALL fields are static and final in interfaces, even if they don't explicitly state that.
@@ -78,7 +89,7 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
/** Returns the lifetime of the summoned creature in ticks. Allows primarily for duration multiplier support, but
* also for example the skeleton legion spell which lasts for 60 seconds instead of the usual 30. Syncing and saving
- * is done automatically. As of Wizardry 1.2, despawning is handled in ISummonedCreature; see
+ * is done automatically. As of Wizardry 2.1, despawning is handled in ISummonedCreature; see
* {@link ISummonedCreature#onDespawn()} for details. */
int getLifetime();
@@ -274,5 +285,49 @@ public interface ISummonedCreature extends IEntityAdditionalSpawnData {
return false;
}
+
+ // Damage system
+
+ @SubscribeEvent
+ public static void onLivingAttackEvent(LivingAttackEvent event){
+
+ // Rather than bother overriding entire attack methods in ISummonedCreature implementations, it's easier (and
+ // more robust) to use LivingAttackEvent to modify the damage source.
+ if(event.getSource().getEntity() instanceof ISummonedCreature){
+
+ EntityLivingBase summoner = ((ISummonedCreature)event.getSource().getEntity()).getCaster();
+
+ if(summoner != null){
+
+ event.setCanceled(true);
+ DamageSource newSource = event.getSource();
+ // Copies over the original DamageType if appropriate.
+ DamageType type = event.getSource() instanceof IElementalDamage ? ((IElementalDamage)event.getSource()).getType() : DamageType.MAGIC;
+ // Copies over the original isRetaliatory flag if appropriate.
+ boolean isRetaliatory = event.getSource() instanceof IElementalDamage && ((IElementalDamage)event.getSource()).isRetaliatory();
+
+ // All summoned creatures are classified as magic, so it makes sense to do it this way.
+ if(event.getSource() instanceof EntityDamageSourceIndirect){
+ newSource = new IndirectMinionDamage(event.getSource().damageType, event.getSource().getSourceOfDamage(), event.getSource().getEntity(), summoner, type, isRetaliatory);
+ }else if(event.getSource() instanceof EntityDamageSource){
+ // Name is copied over so it uses the appropriate vanilla death message
+ newSource = new MinionDamage(event.getSource().damageType, event.getSource().getEntity(), summoner, type, isRetaliatory);
+ }
+
+ // Copy over any relevant 'attributes' the original DamageSource might have had.
+ if(event.getSource().isExplosion()) newSource.setExplosion();
+ if(event.getSource().isFireDamage()) newSource.setFireDamage();
+ if(event.getSource().isProjectile()) newSource.setProjectile();
+
+ // For some reason Minecraft calculates knockback relative to DamageSource#getEntity. In vanilla this
+ // is unnoticeable, but it looks a bit weird with summoned creatures involved - so this fixes that.
+ if(WizardryUtilities.attackEntityWithoutKnockback(event.getEntity(), newSource, event.getAmount())){
+ WizardryUtilities.applyStandardKnockback(event.getSource().getEntity(), event.getEntityLiving());
+ ((ISummonedCreature)event.getSource().getEntity()).onSuccessfulAttack(event.getEntityLiving());
+ }
+
+ }
+ }
+ }
}
diff --git a/src/main/java/electroblob/wizardry/event/DiscoverSpellEvent.java b/src/main/java/electroblob/wizardry/event/DiscoverSpellEvent.java
index 48ca70a9..066ae73d 100644
--- a/src/main/java/electroblob/wizardry/event/DiscoverSpellEvent.java
+++ b/src/main/java/electroblob/wizardry/event/DiscoverSpellEvent.java
@@ -7,10 +7,9 @@ import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
/**
- * [NYI] DiscoverSpellEvent is fired when a player discovers a spell by any method.
+ * DiscoverSpellEvent is fired when a player discovers a spell by any method.
*
- * This event is {@link Cancelable}.
- * If this event is canceled, the spell is not discovered.
+ * This event is {@link Cancelable}. If this event is canceled, the spell is not discovered.
*
* This event does not have a result. {@link HasResult}
*
@@ -21,15 +20,30 @@ import net.minecraftforge.fml.common.eventhandler.Cancelable;
*/
public class DiscoverSpellEvent extends PlayerEvent {
+ private final Spell spell;
+ private final Source source;
+
public DiscoverSpellEvent(EntityPlayer player, Spell spell, Source source) {
super(player);
+ this.spell = spell;
+ this.source = source;
+ }
+
+ /** Returns the spell that is being discovered. */
+ public Spell getSpell(){
+ return spell;
+ }
+
+ /** Returns the method used to discover the spell. */
+ public Source getSource(){
+ return source;
}
public enum Source {
/** Signifies that the spell was discovered by trying to cast it. */ CASTING,
/** Signifies that the spell was discovered using a scroll of identification. */ IDENTIFICATION_SCROLL,
/** Signifies that the spell was discovered using commands. */ COMMAND,
- /** Signifies that the spell was discovered by some other means (. */ OTHER
+ /** Signifies that the spell was discovered by some other means. */ OTHER
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/electroblob/wizardry/event/SpellBindEvent.java b/src/main/java/electroblob/wizardry/event/SpellBindEvent.java
index f1a88f43..dd6cf322 100644
--- a/src/main/java/electroblob/wizardry/event/SpellBindEvent.java
+++ b/src/main/java/electroblob/wizardry/event/SpellBindEvent.java
@@ -1,16 +1,18 @@
package electroblob.wizardry.event;
+import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraftforge.common.MinecraftForge;
-import net.minecraftforge.event.entity.player.PlayerEvent;
+import net.minecraftforge.event.entity.player.PlayerContainerEvent;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
+import net.minecraftforge.fml.common.eventhandler.Event.HasResult;
/**
- * [NYI] SpellBindEvent is fired when a player presses the apply button in the arcane workbench.
+ * SpellBindEvent is fired when a player presses the apply button in the arcane workbench. Note that this
+ * event is only fired on the server side.
*
- * This event is {@link Cancelable}.
- * If this event is canceled, no further processing takes place: spells are not bound, upgrades are not applied and
- * crystals are not consumed.
+ * This event is {@link Cancelable}. If this event is canceled, no further processing takes place: spells are not bound,
+ * upgrades are not applied and crystals are not consumed.
*
* This event does not have a result. {@link HasResult}
*
@@ -19,10 +21,11 @@ import net.minecraftforge.fml.common.eventhandler.Cancelable;
* @author Electroblob
* @since Wizardry 2.1
*/
-public class SpellBindEvent extends PlayerEvent {
+@Cancelable
+public class SpellBindEvent extends PlayerContainerEvent {
- public SpellBindEvent(EntityPlayer player) {
- super(player);
+ public SpellBindEvent(EntityPlayer player, ContainerArcaneWorkbench container) {
+ super(player, container);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/electroblob/wizardry/event/SpellCastEvent.java b/src/main/java/electroblob/wizardry/event/SpellCastEvent.java
index 2c5346df..c1072eda 100644
--- a/src/main/java/electroblob/wizardry/event/SpellCastEvent.java
+++ b/src/main/java/electroblob/wizardry/event/SpellCastEvent.java
@@ -1,12 +1,17 @@
package electroblob.wizardry.event;
+import electroblob.wizardry.entity.living.ISpellCaster;
import electroblob.wizardry.spell.Spell;
+import electroblob.wizardry.util.SpellModifiers;
import net.minecraft.entity.EntityLivingBase;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.living.LivingEvent;
+import net.minecraftforge.fml.common.eventhandler.Cancelable;
/**
- * [NYI] SpellCastEvent is the parent event class for all spell casting events.
+ * SpellCastEvent is the parent event class for all spell casting events. Methods which subscribe to this event
+ * will receive all three child events (it is recommended that you use {@link SpellCastEvent.Pre},
+ * {@link SpellCastEvent.Post} or {@link SpellCastEvent.Tick}, depending on the application).
*
* This event is fired on the {@link MinecraftForge#EVENT_BUS}.
*
@@ -14,9 +19,121 @@ import net.minecraftforge.event.entity.living.LivingEvent;
* @since Wizardry 2.1
*/
public abstract class SpellCastEvent extends LivingEvent {
+
+ private final Spell spell;
+ private final SpellModifiers modifiers;
+ private final Source source;
- public SpellCastEvent(EntityLivingBase entity, Spell spell) {
- super(entity);
+ public SpellCastEvent(EntityLivingBase caster, Spell spell, SpellModifiers modifiers, Source source) {
+ super(caster);
+ this.spell = spell;
+ this.modifiers = modifiers;
+ this.source = source;
+ }
+
+ /** Returns the spell being cast. */
+ public Spell getSpell(){
+ return spell;
+ }
+
+ /** Returns the modifiers for the spell being cast. */
+ public SpellModifiers getModifiers(){
+ return modifiers;
+ }
+
+ /** Returns the source of the spell being cast. */
+ public Source getSource(){
+ return source;
+ }
+
+ public enum Source {
+ /** Signifies that the spell was cast using a wand. */ WAND,
+ /** Signifies that the spell was cast using a scroll. */ SCROLL,
+ /** Signifies that the spell was cast using commands. */ COMMAND,
+ /** Signifies that the spell was cast by an {@link ISpellCaster}. */ NPC,
+ /** Signifies that the spell was cast by some other means. */ OTHER
}
+ /**
+ * SpellCastEvent.Pre is fired just before a spell is cast. Use this event to change the spell modifiers and
+ * generally alter the behaviour of the spell, or stop it from being cast entirely. For example, wizardry uses this
+ * event to cancel spells cast by entities that have the arcane jammer effect. Note that for wands, this is called
+ * before mana, tier and cooldowns are checked. Also note that this event is only fired once for continuous
+ * spells, when they start casting.
+ *
+ * This event is {@link Cancelable}. If this event is canceled, the spell is not cast, mana is not consumed, and the
+ * right-click action that caused it (if any) returns a result of FAIL, meaning that the right-click is passed to
+ * the block/entity in front of the player, if any.
+ *
+ * This event does not have a result. {@link HasResult}
+ *
+ * This event is fired on the {@link MinecraftForge#EVENT_BUS}.
+ *
+ * @author Electroblob
+ * @since Wizardry 2.1
+ */
+ @Cancelable
+ public static class Pre extends SpellCastEvent {
+
+ public Pre(EntityLivingBase caster, Spell spell, SpellModifiers modifiers, Source source) {
+ super(caster, spell, modifiers, source);
+ }
+
+ }
+
+ /**
+ * SpellCastEvent.Post is fired just after a spell is cast. Use this event for any processing which depends
+ * on whether the spell succeeds, and does not affect the spell itself. For example, wizardry uses this event
+ * to keep track of spellcasting stats. Note that although this event is fired from both sides, it is not fired from
+ * common code; rather, both sides fire it separately, so timing is not guaranteed. Also note that this event is
+ * only fired once for continuous spells, after the first casting tick.
+ *
+ * This event is not {@link Cancelable}.
+ *
+ * This event does not have a result. {@link HasResult}
+ *
+ * This event is fired on the {@link MinecraftForge#EVENT_BUS}.
+ *
+ * @author Electroblob
+ * @since Wizardry 2.1
+ */
+ public static class Post extends SpellCastEvent {
+
+ public Post(EntityLivingBase caster, Spell spell, SpellModifiers modifiers, Source source) {
+ super(caster, spell, modifiers, source);
+ }
+
+ }
+
+ /**
+ * SpellCastEvent.Tick is fired each tick while a continuous spell is being cast.
+ *
+ * This event is {@link Cancelable}. If this event is canceled, the spell is not cast, mana is not consumed, and the
+ * spell casting is interrupted. Cancelling this event on the client side will stop particles being spawned, but
+ * will not interrupt spell casting.
+ *
+ * This event does not have a result. {@link HasResult}
+ *
+ * This event is fired on the {@link MinecraftForge#EVENT_BUS}.
+ *
+ * @author Electroblob
+ * @since Wizardry 2.1
+ */
+ @Cancelable
+ public static class Tick extends SpellCastEvent {
+
+ private final int count;
+
+ public Tick(EntityLivingBase caster, Spell spell, SpellModifiers modifiers, Source source, int count) {
+ super(caster, spell, modifiers, source);
+ this.count = count;
+ }
+
+ /** Returns the number of ticks this (continuous) spell has already been cast for. */
+ public int getCount(){
+ return count;
+ }
+
+ }
+
}
diff --git a/src/main/java/electroblob/wizardry/item/IConjuredItem.java b/src/main/java/electroblob/wizardry/item/IConjuredItem.java
index 6ae44c6e..ee8a0845 100644
--- a/src/main/java/electroblob/wizardry/item/IConjuredItem.java
+++ b/src/main/java/electroblob/wizardry/item/IConjuredItem.java
@@ -1,11 +1,17 @@
package electroblob.wizardry.item;
+import net.minecraft.entity.item.EntityItem;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
+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;
/** 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. */
+@Mod.EventBusSubscriber
public interface IConjuredItem {
/** The NBT tag key used to store the duration multiplier for conjured items. */
@@ -29,4 +35,23 @@ public interface IConjuredItem {
/** 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. */
public int getBaseDuration();
+
+ @SubscribeEvent
+ public static void onLivingDropsEvent(LivingDropsEvent event){
+ // Destroys conjured items if their caster dies.
+ for(EntityItem item : event.getDrops()){
+ if(item.getEntityItem().getItem() instanceof IConjuredItem){
+ item.setDead();
+ }
+ }
+ }
+
+ @SubscribeEvent
+ public static void onItemTossEvent(ItemTossEvent event){
+ // Prevents conjured items being thrown by dragging and dropping outside the inventory.
+ if(event.getEntityItem().getEntityItem().getItem() instanceof IConjuredItem){
+ event.setCanceled(true);
+ event.getPlayer().inventory.addItemStackToInventory(event.getEntityItem().getEntityItem());
+ }
+ }
}
diff --git a/src/main/java/electroblob/wizardry/item/ItemIdentificationScroll.java b/src/main/java/electroblob/wizardry/item/ItemIdentificationScroll.java
index da85487d..265bf225 100644
--- a/src/main/java/electroblob/wizardry/item/ItemIdentificationScroll.java
+++ b/src/main/java/electroblob/wizardry/item/ItemIdentificationScroll.java
@@ -3,6 +3,7 @@ package electroblob.wizardry.item;
import java.util.List;
import electroblob.wizardry.WizardData;
+import electroblob.wizardry.event.DiscoverSpellEvent;
import electroblob.wizardry.registry.WizardryAchievements;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.spell.Spell;
@@ -17,6 +18,7 @@ import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World;
+import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@@ -54,15 +56,17 @@ public class ItemIdentificationScroll extends Item {
if((stack1.getItem() instanceof ItemSpellBook || stack1.getItem() instanceof ItemScroll)
&& !properties.hasSpellBeenDiscovered(spell)){
- // Identification scrolls give the chat readout in creative mode, otherwise it looks like
- // nothing happens!
- properties.discoverSpell(spell);
- player.addStat(WizardryAchievements.identify_spell);
- player.playSound(SoundEvents.ENTITY_PLAYER_LEVELUP, 1.25f, 1);
- if(!player.capabilities.isCreativeMode) stack.stackSize--;
- if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("spell.discover", spell.getNameForTranslationFormatted()));
+ 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);
+ player.addStat(WizardryAchievements.identify_spell);
+ player.playSound(SoundEvents.ENTITY_PLAYER_LEVELUP, 1.25f, 1);
+ if(!player.capabilities.isCreativeMode) stack.stackSize--;
+ if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("spell.discover", spell.getNameForTranslationFormatted()));
- return new ActionResult(EnumActionResult.SUCCESS, stack);
+ return new ActionResult(EnumActionResult.SUCCESS, stack);
+ }
}
}
}
diff --git a/src/main/java/electroblob/wizardry/item/ItemScroll.java b/src/main/java/electroblob/wizardry/item/ItemScroll.java
index e238b5b8..b77a95f6 100644
--- a/src/main/java/electroblob/wizardry/item/ItemScroll.java
+++ b/src/main/java/electroblob/wizardry/item/ItemScroll.java
@@ -2,24 +2,23 @@ package electroblob.wizardry.item;
import java.util.List;
-import electroblob.wizardry.WizardData;
import electroblob.wizardry.Wizardry;
+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.WizardryPotions;
import electroblob.wizardry.registry.WizardryTabs;
import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.SpellModifiers;
import net.minecraft.creativetab.CreativeTabs;
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;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumHand;
-import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World;
+import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@@ -36,7 +35,6 @@ public class ItemScroll extends Item {
@Override
@SideOnly(Side.CLIENT)
public void getSubItems(Item item, CreativeTabs par2CreativeTabs, List list){
- // Isn't this sooooo much neater with the filter thing?
for(Spell spell : Spell.getSpells(Spell.nonContinuousSpells)){
list.add(new ItemStack(item, 1, spell.id()));
}
@@ -70,59 +68,42 @@ public class ItemScroll extends Item {
@Override
public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){
- if(player.isPotionActive(WizardryPotions.arcane_jammer)) return new ActionResult(EnumActionResult.FAIL, stack);;
-
Spell spell = Spell.get(stack.getItemDamage());
-
- // If a spell is disabled in the config, it will not work.
- if(!spell.isEnabled()){
- if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("spell.disabled", spell.getNameForTranslationFormatted()));
+ // 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(EnumActionResult.FAIL, stack);
}
-
+
if(!spell.isContinuous){
- /*
- if(spell.chargeup > 0 && !entityplayer.isUsingItem()){
- // Spells with a chargeup time are now handled separately.
- entityplayer.setItemInUse(stack, this.getMaxItemUseDuration(stack));
- return stack;
- }
- */
-
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(), new SpellModifiers());
+ IMessage msg = new PacketCastSpell.Message(player.getEntityId(), hand, spell.id(), modifiers);
WizardryPacketHandler.net.sendToDimension(msg, world.provider.getDimension());
}
- if(!player.capabilities.isCreativeMode && !WizardData.get(player).hasSpellBeenDiscovered(spell) && Wizardry.settings.discoveryMode){
- player.playSound(SoundEvents.ENTITY_PLAYER_LEVELUP, 1.25f, 1);
- if(!player.worldObj.isRemote) player.addChatMessage(new TextComponentTranslation("spell.discover", spell.getNameForTranslationFormatted()));
- }
- WizardData.get(player).discoverSpell(spell);
-
+ // Scrolls are consumed upon successful use in survival mode
if(!player.capabilities.isCreativeMode) stack.stackSize--;
return new ActionResult(EnumActionResult.SUCCESS, stack);
}
- // Client-inconsistent spell casting. The code inside the else if statement only runs client-side.
// This else if check was bugging me for AGES! I can't believe I didn't compare to ItemWand before.
}else if(!spell.doesSpellRequirePacket()){
- // This is all that needs to happen, because everything above works fine on just the server side.
- if(spell.cast(world, player, hand, 0, new SpellModifiers())){
- // Added in version 1.1.3 to fix the client-side spell discovery not updating for spells with the
- // packet optimisation.
- if(WizardData.get(player) != null){
- WizardData.get(player).discoverSpell(spell);
- }
-
- new ActionResult(EnumActionResult.SUCCESS, stack);
+ // 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(EnumActionResult.SUCCESS, stack);
}
}
}
diff --git a/src/main/java/electroblob/wizardry/item/ItemWand.java b/src/main/java/electroblob/wizardry/item/ItemWand.java
index 59d126e5..6f1f99b5 100644
--- a/src/main/java/electroblob/wizardry/item/ItemWand.java
+++ b/src/main/java/electroblob/wizardry/item/ItemWand.java
@@ -9,6 +9,8 @@ import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.constants.Tier;
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.WizardryAchievements;
@@ -24,7 +26,6 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
-import net.minecraft.init.SoundEvents;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.item.EnumAction;
import net.minecraft.item.Item;
@@ -32,10 +33,10 @@ 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.RayTraceResult;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World;
+import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@@ -79,12 +80,12 @@ public class ItemWand extends Item {
public net.minecraft.client.gui.FontRenderer getFontRenderer(ItemStack stack){
return Wizardry.proxy.getFontRenderer(stack);
}
-
+
@Override
- @SideOnly(Side.CLIENT)
- public void getSubItems(Item parItem, CreativeTabs parTab, List parListSubItems){
- parListSubItems.add(new ItemStack(this, 1));
- }
+ @SideOnly(Side.CLIENT)
+ public void getSubItems(Item parItem, CreativeTabs parTab, List parListSubItems){
+ parListSubItems.add(new ItemStack(this, 1));
+ }
// Max damage is modifiable with upgrades.
@Override
@@ -95,13 +96,6 @@ public class ItemWand extends Item {
@Override
public void onCreated(ItemStack stack, World par2World, EntityPlayer par3EntityPlayer){
- /* Removed because of mana flasks, which would cause a new book to be given each time a mana flask
- * is crafted with the wand. Handbook is now given on acquiring a crystal.
- ExtendedPlayer properties = ExtendedPlayer.get(par3EntityPlayer);
- if(properties != null && !properties.handbookGiven){
- par3EntityPlayer.inventory.addItemStackToInventory(new ItemStack(Wizardry.wizardHandbook));
- properties.handbookGiven = true;
- }*/
par3EntityPlayer.addStat(WizardryAchievements.arcane_initiate);
}
@@ -124,19 +118,19 @@ public class ItemWand extends Item {
((EntityPlayer)entity).addStat(WizardryAchievements.elemental);
}
}
-
+
@Override
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...
-
+
if(oldStack != null || newStack != null){
// We only care about the situation where we specifically want the animation NOT to play.
if(oldStack.getItem() == newStack.getItem() && !slotChanged
&& oldStack.getItem() instanceof ItemWand && newStack.getItem() instanceof ItemWand
&& WandHelper.getCurrentSpell(oldStack) == WandHelper.getCurrentSpell(newStack)) return false;
}
-
+
return super.shouldCauseReequipAnimation(oldStack, newStack, slotChanged);
}
@@ -174,33 +168,30 @@ public class ItemWand extends Item {
public String getItemStackDisplayName(ItemStack stack){
return (this.element == null ? "" : this.element.getFormattingCode()) + super.getItemStackDisplayName(stack);
}
-
+
// Continuous spells use the onUsingItemTick method instead of this one.
/* An important thing to note about this method: it is only called on the server and the client of the player
- * holding the item. This means if you spawn particles here they will not show up on other players' screens.
- * Instead, this must be done via packets. */
+ * holding the item (I call this client-inconsistency). This means if you spawn particles here they will not show up
+ * on other players' screens. Instead, this must be done via packets. */
@Override
public ActionResult onItemRightClick(ItemStack stack, World world, EntityPlayer player, EnumHand hand){
-
+
+ // Alternate right-click function; overrides spell casting.
if(this.selectMinionTarget(player, world)) return new ActionResult(EnumActionResult.SUCCESS, stack);
-
- if(player.isPotionActive(WizardryPotions.arcane_jammer)) return new ActionResult(EnumActionResult.FAIL, stack);
-
+
Spell spell = WandHelper.getCurrentSpell(stack);
-
- // If a spell is disabled in the config, it will not work.
- if(!spell.isEnabled()){
- if(!world.isRemote) player.addChatMessage(new TextComponentTranslation("spell.disabled", spell.getNameForTranslationFormatted()));
+ 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(EnumActionResult.FAIL, stack);
}
- // This is here to start the inUse thing, otherwise the onItemUsingTick method will not fire.
- // If the castSpell method then returns false nothing will happen (continuous spells have no EnumAction
- // either so setting the item in use has no direct visible effect).
- // Edit: Strictly speaking this is not true but the spells that do have an action (shield, shadow ward and levitation)
- // will never return false.
+ // 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(EnumActionResult.SUCCESS, stack);
}
// Conditions for the spell to be attempted. The tier check is a failsafe; it should never be false unless the
@@ -211,14 +202,13 @@ public class ItemWand extends Item {
// Checks that the spell is not in cooldown or that the player is in creative mode
&& (WandHelper.getCurrentCooldown(stack) == 0 || player.capabilities.isCreativeMode)){
- // = Spell modifiers =
- SpellModifiers modifiers = this.calculateModifiers(stack, spell);
-
// 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()){
@@ -230,17 +220,6 @@ public class ItemWand extends Item {
}
player.setActiveHand(hand);
-
- WizardData data = WizardData.get(player);
-
- // = Discovery =
- if(data != null && !player.capabilities.isCreativeMode && !data.hasSpellBeenDiscovered(spell) && Wizardry.settings.discoveryMode){
- // We are only server side here, so we need to play the sound on the server side to everyone.
- world.playSound(null, player.posX, player.posY, player.posZ, SoundEvents.ENTITY_PLAYER_LEVELUP,
- SoundCategory.PLAYERS, 1.25f, 1);
- player.addChatMessage(new TextComponentTranslation("spell.discover", spell.getNameForTranslationFormatted()));
- }
- WizardData.get(player).discoverSpell(spell);
// = Cooldown =
// Spells only have a cooldown in survival
@@ -264,53 +243,42 @@ public class ItemWand extends Item {
return new ActionResult(EnumActionResult.SUCCESS, stack);
}
-
- // Client-inconsistent spell casting. The code inside the else if statement only runs client-side.
+
}else if(!spell.doesSpellRequirePacket()){
- // This is all that needs to happen, because everything above works fine on just the server side.
- if(spell.cast(world, player, hand, 0, new SpellModifiers())){
- // Added in version 1.1.3 to fix the client-side spell discovery not updating for spells with the
- // packet optimisation.
- if(WizardData.get(player) != null){
- WizardData.get(player).discoverSpell(spell);
- }
-
- new ActionResult(EnumActionResult.SUCCESS, stack);
+ // 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(EnumActionResult.SUCCESS, stack);
}
}
}
-
+
return new ActionResult(EnumActionResult.FAIL, stack);
}
// For continuous spells. The count argument actually decrements by 1 each tick.
@Override
public void onUsingTick(ItemStack stack, EntityLivingBase user, int count){
-
+
if(user instanceof EntityPlayer){
-
+
EntityPlayer player = (EntityPlayer)user;
Spell spell = WandHelper.getCurrentSpell(stack);
-
- WizardData data = WizardData.get(player);
-
+ SpellModifiers modifiers = this.calculateModifiers(stack, spell);
+ 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())){
-
- // = Spell modifiers =
- SpellModifiers modifiers = this.calculateModifiers(stack, spell);
-
- if(spell.cast(player.worldObj, player, player.getActiveHand(), stack.getMaxItemUseDuration() - count, modifiers)){
-
- // = Discovery =
- if(data != null && !player.capabilities.isCreativeMode && !data.hasSpellBeenDiscovered(spell) && Wizardry.settings.discoveryMode){
- player.playSound(SoundEvents.ENTITY_PLAYER_LEVELUP, 1.25f, 1);
- if(!player.worldObj.isRemote) player.addChatMessage(new TextComponentTranslation("spell.discover", spell.getNameForTranslationFormatted()));
- }
- data.discoverSpell(spell);
-
+
+ if(spell.cast(player.worldObj, player, player.getActiveHand(), castingTick, modifiers)){
+
+ if(castingTick == 0) MinecraftForge.EVENT_BUS.post(new SpellCastEvent.Post(player, spell, modifiers, Source.WAND));
+
// = Mana cost =
// Divides the mana cost over a second appropriately; since damage is an integer it cannot
// just be divided by 20.
@@ -320,57 +288,32 @@ public class ItemWand extends Item {
// 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 armourPieces = getMatchingArmourCount(player, spell);
-
+
switch(armourPieces){
-
+
case 0: stack.damageItem(spell.cost/5, player);
break;
-
+
case 1: if(tickNumber != 17) stack.damageItem(spell.cost/5, player);
break;
-
+
case 2: if(tickNumber != 9 && tickNumber != 17) stack.damageItem(spell.cost/5, player);
break;
-
+
case 3: if(tickNumber != 5 && tickNumber != 13 && tickNumber != 17) stack.damageItem(spell.cost/5, player);
break;
-
+
case 4: if(tickNumber == 1) stack.damageItem(spell.cost/5, player);
break;
-
+
}
}
}
}
}
}
-
- /** Returns a SpellModifiers object with the appropriate modifiers applied for the given ItemStack and Spell. */
- protected SpellModifiers calculateModifiers(ItemStack stack, Spell spell){
-
- SpellModifiers modifiers = new SpellModifiers();
-
- // Now we only need to add multipliers if they are not 1.
- int level = WandHelper.getUpgradeLevel(stack, WizardryItems.range_upgrade);
- if(level > 0) modifiers.set(WizardryItems.range_upgrade, 1.0f + level * Constants.RANGE_INCREASE_PER_LEVEL, true);
-
- level = WandHelper.getUpgradeLevel(stack, WizardryItems.duration_upgrade);
- if(level > 0) modifiers.set(WizardryItems.duration_upgrade, 1.0f + level * Constants.DURATION_INCREASE_PER_LEVEL, false);
-
- level = WandHelper.getUpgradeLevel(stack, WizardryItems.blast_upgrade);
- 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.DAMAGE, 1.0f + (this.tier.level + 1) * Constants.DAMAGE_INCREASE_PER_TIER, true);
- }
-
- return modifiers;
- }
@Override
public boolean itemInteractionForEntity(ItemStack stack, EntityPlayer player, EntityLivingBase entity, EnumHand hand){
@@ -385,36 +328,61 @@ public class ItemWand extends Item {
return false;
}
-
+
+ /** Returns a SpellModifiers object with the appropriate modifiers applied for the given ItemStack and Spell. */
+ protected SpellModifiers calculateModifiers(ItemStack stack, Spell spell){
+
+ SpellModifiers modifiers = new SpellModifiers();
+
+ // Now we only need to add multipliers if they are not 1.
+ int level = WandHelper.getUpgradeLevel(stack, WizardryItems.range_upgrade);
+ if(level > 0) modifiers.set(WizardryItems.range_upgrade, 1.0f + level * Constants.RANGE_INCREASE_PER_LEVEL, true);
+
+ level = WandHelper.getUpgradeLevel(stack, WizardryItems.duration_upgrade);
+ if(level > 0) modifiers.set(WizardryItems.duration_upgrade, 1.0f + level * Constants.DURATION_INCREASE_PER_LEVEL, false);
+
+ level = WandHelper.getUpgradeLevel(stack, WizardryItems.blast_upgrade);
+ 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.DAMAGE, 1.0f + (this.tier.level + 1) * Constants.DAMAGE_INCREASE_PER_TIER, true);
+ }
+
+ 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);
-
+
if(rayTrace != null && rayTrace.entityHit instanceof EntityLivingBase){
-
+
EntityLivingBase entity = (EntityLivingBase)rayTrace.entityHit;
-
+
// Sets the selected minion's target to the right-clicked entity
if(player.isSneaking() && WizardData.get(player) != null && WizardData.get(player).selectedMinion != null){
-
+
ISummonedCreature minion = WizardData.get(player).selectedMinion.get();
-
+
if(minion instanceof EntityLiving && minion != entity){
// There is now only the new AI! (which greatly improves things)
((EntityLiving)minion).setAttackTarget(entity);
@@ -424,7 +392,7 @@ public class ItemWand extends Item {
}
}
}
-
+
return false;
}
}
diff --git a/src/main/java/electroblob/wizardry/loot/RandomSpell.java b/src/main/java/electroblob/wizardry/loot/RandomSpell.java
index 90c7c661..0633d22e 100644
--- a/src/main/java/electroblob/wizardry/loot/RandomSpell.java
+++ b/src/main/java/electroblob/wizardry/loot/RandomSpell.java
@@ -4,6 +4,8 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Random;
+import org.apache.commons.lang3.ArrayUtils;
+
import com.google.gson.JsonArray;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonElement;
@@ -74,7 +76,7 @@ public class RandomSpell extends LootFunction {
@Override
public ItemStack apply(ItemStack stack, Random random, LootContext context){
- if(!(stack.getItem() instanceof ItemSpellBook) || !(stack.getItem() instanceof ItemScroll))
+ if(!(stack.getItem() instanceof ItemSpellBook) && !(stack.getItem() instanceof ItemScroll))
Wizardry.logger.warn("Applying the random_spell loot function to an item that isn't a spell book or scroll.");
Tier tier;
@@ -96,7 +98,12 @@ public class RandomSpell extends LootFunction {
// Elements aren't weighted
if(elements == null || elements.isEmpty()){
- element = Element.values()[random.nextInt(Element.values().length)];
+ // Element can only be MAGIC if tier is BASIC
+ if(tier == Tier.BASIC){
+ element = Element.values()[random.nextInt(Element.values().length)];
+ }else{
+ element = ArrayUtils.removeElement(Element.values(), Element.MAGIC)[random.nextInt(Element.values().length)];
+ }
}else{
// In theory, swapping this line to the commented one should make absolutely no difference.
element = elements.get(random.nextInt(tiers.size()));
diff --git a/src/main/java/electroblob/wizardry/packet/PacketControlInput.java b/src/main/java/electroblob/wizardry/packet/PacketControlInput.java
index fb4b01a5..afa52a4f 100644
--- a/src/main/java/electroblob/wizardry/packet/PacketControlInput.java
+++ b/src/main/java/electroblob/wizardry/packet/PacketControlInput.java
@@ -3,7 +3,6 @@ package electroblob.wizardry.packet;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.packet.PacketControlInput.Message;
import electroblob.wizardry.tileentity.ContainerArcaneWorkbench;
-import electroblob.wizardry.tileentity.TileEntityArcaneWorkbench;
import electroblob.wizardry.util.WandHelper;
import io.netty.buffer.ByteBuf;
import net.minecraft.entity.player.EntityPlayerMP;
@@ -37,8 +36,7 @@ public class PacketControlInput implements IMessageHandler {
case APPLY_BUTTON:
- TileEntityArcaneWorkbench tileentity = ((ContainerArcaneWorkbench)player.openContainer).tileEntityArcaneWorkbench;
- tileentity.onApplyButtonPressed(player);
+ ((ContainerArcaneWorkbench)player.openContainer).onApplyButtonPressed(player);
break;
case NEXT_SPELL_KEY:
diff --git a/src/main/java/electroblob/wizardry/potion/ICustomPotionParticles.java b/src/main/java/electroblob/wizardry/potion/ICustomPotionParticles.java
index 4b66e59d..dcb19997 100644
--- a/src/main/java/electroblob/wizardry/potion/ICustomPotionParticles.java
+++ b/src/main/java/electroblob/wizardry/potion/ICustomPotionParticles.java
@@ -1,6 +1,10 @@
package electroblob.wizardry.potion;
+import net.minecraft.potion.PotionEffect;
import net.minecraft.world.World;
+import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
/**
* Interface for potion effects that spawn custom particles instead of (or as well as) the vanilla 'swirly' particles.
@@ -8,6 +12,7 @@ import net.minecraft.world.World;
* @since Wizardry 1.2
*/
// TODO: Backport.
+@Mod.EventBusSubscriber
public interface ICustomPotionParticles {
/**
@@ -20,4 +25,21 @@ public interface ICustomPotionParticles {
*/
void spawnCustomParticle(World world, double x, double y, double z);
+ @SubscribeEvent
+ public static void onLivingUpdateEvent(LivingUpdateEvent event){
+ if(event.getEntityLiving().worldObj.isRemote){
+ // Behold the power of interfaces!
+ for(PotionEffect effect : event.getEntityLiving().getActivePotionEffects()){
+
+ if(effect.getPotion() instanceof ICustomPotionParticles && effect.doesShowParticles()){
+
+ double x = event.getEntityLiving().posX + (event.getEntityLiving().worldObj.rand.nextDouble() - 0.5)*event.getEntityLiving().width;
+ double y = event.getEntityLiving().getEntityBoundingBox().minY + event.getEntityLiving().worldObj.rand.nextDouble()*event.getEntityLiving().height;
+ double z = event.getEntityLiving().posZ + (event.getEntityLiving().worldObj.rand.nextDouble() - 0.5)*event.getEntityLiving().width;
+
+ ((ICustomPotionParticles)effect.getPotion()).spawnCustomParticle(event.getEntityLiving().worldObj, x, y, z);
+ }
+ }
+ }
+ }
}
diff --git a/src/main/java/electroblob/wizardry/potion/PotionDecay.java b/src/main/java/electroblob/wizardry/potion/PotionDecay.java
index 8386115a..6e767866 100644
--- a/src/main/java/electroblob/wizardry/potion/PotionDecay.java
+++ b/src/main/java/electroblob/wizardry/potion/PotionDecay.java
@@ -1,8 +1,12 @@
package electroblob.wizardry.potion;
+import java.util.List;
+
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Constants;
+import electroblob.wizardry.entity.construct.EntityDecay;
import electroblob.wizardry.util.WizardryUtilities;
+import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.potion.Potion;
@@ -32,8 +36,25 @@ public class PotionDecay extends Potion {
}
@Override
- public void performEffect(EntityLivingBase entitylivingbase, int strength) {
- entitylivingbase.attackEntityFrom(DamageSource.wither, 1);
+ public void performEffect(EntityLivingBase target, int strength) {
+
+ target.attackEntityFrom(DamageSource.wither, 1);
+
+ if(target.onGround && target.ticksExisted % Constants.DECAY_SPREAD_INTERVAL == 0){
+
+ List entities = target.worldObj.getEntitiesWithinAABBExcludingEntity(target, target.getEntityBoundingBox());
+
+ boolean flag = true;
+
+ for(Entity entity : entities){
+ if(entity instanceof EntityDecay) flag = false;
+ }
+
+ if(flag){
+ // The victim spreading the decay is the 'caster' here, so that it can actually wear off, otherwise it just gets infected with its own decay and the effect lasts forever.
+ target.worldObj.spawnEntityInWorld(new EntityDecay(target.worldObj, target.posX, target.posY, target.posZ, target));
+ }
+ }
}
@Override
diff --git a/src/main/java/electroblob/wizardry/potion/PotionFrost.java b/src/main/java/electroblob/wizardry/potion/PotionFrost.java
index 2b2c7783..152adfab 100644
--- a/src/main/java/electroblob/wizardry/potion/PotionFrost.java
+++ b/src/main/java/electroblob/wizardry/potion/PotionFrost.java
@@ -2,6 +2,7 @@ package electroblob.wizardry.potion;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Constants;
+import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.util.WizardryParticleType;
import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.entity.EntityLivingBase;
@@ -10,9 +11,13 @@ import net.minecraft.potion.Potion;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
+import net.minecraftforge.event.entity.player.PlayerEvent.BreakSpeed;
+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;
+@Mod.EventBusSubscriber
public class PotionFrost extends Potion implements ICustomPotionParticles {
private static final ResourceLocation ICON = new ResourceLocation(Wizardry.MODID, "textures/gui/frost_icon.png");
@@ -50,4 +55,12 @@ public class PotionFrost extends Potion implements ICustomPotionParticles {
WizardryUtilities.drawTexturedRect(x + 3, y + 3, 0, 0, 18, 18, 18, 18);
}
+ @SubscribeEvent
+ public static void onBreakSpeedEvent(BreakSpeed event){
+ if(event.getEntityPlayer().isPotionActive(WizardryPotions.frost)){
+ // Amplifier + 1 because it starts at 0
+ event.setNewSpeed(event.getOriginalSpeed() * (1 - Constants.FROST_FATIGUE_PER_LEVEL*(event.getEntityPlayer().getActivePotionEffect(WizardryPotions.frost).getAmplifier() + 1)));
+ }
+ }
+
}
diff --git a/src/main/java/electroblob/wizardry/registry/Spells.java b/src/main/java/electroblob/wizardry/registry/Spells.java
index f5205f29..a59e7538 100644
--- a/src/main/java/electroblob/wizardry/registry/Spells.java
+++ b/src/main/java/electroblob/wizardry/registry/Spells.java
@@ -19,7 +19,8 @@ import net.minecraftforge.fml.common.registry.RegistryBuilder;
// stuff during the registry events (or whenever), whilst still having a final field (which is important, not only
// because it makes the text go bold, but also because it stops anyone fiddling with your fields). "Why would I want to
// initialise things within the registry events?", I hear you ask - well, for one, custom registries don't like it if
-// you haven't created the registry before you start calling constructors of classes extending IForgeRegistryEntry.Impl.
+// you haven't created the registry before you start calling constructors of classes extending IForgeRegistryEntry.Impl,
+// and secondly,
@ObjectHolder(Wizardry.MODID)
@Mod.EventBusSubscriber
public final class Spells {
diff --git a/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java b/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java
index 30bd9cca..163e2f42 100644
--- a/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java
+++ b/src/main/java/electroblob/wizardry/spell/ArcaneJammer.java
@@ -5,6 +5,7 @@ import electroblob.wizardry.constants.Element;
import electroblob.wizardry.constants.SpellType;
import electroblob.wizardry.constants.Tier;
import electroblob.wizardry.entity.living.EntityWizard;
+import electroblob.wizardry.event.SpellCastEvent;
import electroblob.wizardry.registry.WizardryAchievements;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryPotions;
@@ -21,7 +22,10 @@ import net.minecraft.util.EnumHand;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+@Mod.EventBusSubscriber
public class ArcaneJammer extends Spell {
public ArcaneJammer() {
@@ -91,5 +95,11 @@ public class ArcaneJammer extends Spell {
public boolean canBeCastByNPCs(){
return true;
}
+
+ @SubscribeEvent
+ public static void onSpellCastPreEvent(SpellCastEvent.Pre event){
+ // Arcane jammer prevents spell casting.
+ if(event.getEntityLiving().isPotionActive(WizardryPotions.arcane_jammer)) event.setCanceled(true);
+ }
}
diff --git a/src/main/java/electroblob/wizardry/spell/Clairvoyance.java b/src/main/java/electroblob/wizardry/spell/Clairvoyance.java
index a9418c4d..749aefab 100644
--- a/src/main/java/electroblob/wizardry/spell/Clairvoyance.java
+++ b/src/main/java/electroblob/wizardry/spell/Clairvoyance.java
@@ -5,11 +5,14 @@ import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.constants.SpellType;
import electroblob.wizardry.constants.Tier;
+import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.packet.PacketClairvoyance;
import electroblob.wizardry.packet.WizardryPacketHandler;
+import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.util.SpellModifiers;
+import electroblob.wizardry.util.WandHelper;
import electroblob.wizardry.util.WizardryParticleType;
import electroblob.wizardry.util.WizardryPathFinder;
import electroblob.wizardry.util.WizardryUtilities;
@@ -18,6 +21,7 @@ import net.minecraft.entity.monster.EntityZombie;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.EnumAction;
+import net.minecraft.item.ItemStack;
import net.minecraft.pathfinding.Path;
import net.minecraft.pathfinding.PathNodeType;
import net.minecraft.pathfinding.PathPoint;
@@ -25,7 +29,11 @@ import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World;
+import net.minecraftforge.event.entity.player.PlayerInteractEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+@Mod.EventBusSubscriber
public class Clairvoyance extends Spell {
/** The number of ticks it takes each path particle to move from one path point to the next. */
@@ -123,5 +131,33 @@ public class Clairvoyance extends Spell {
Wizardry.proxy.spawnParticle(WizardryParticleType.PATH, world, point.xCoord + 0.5, point.yCoord + 0.5, point.zCoord + 0.5, 0, 0, 0, (int)(1800*durationMultiplier), 1, 1, 1);
}
+
+ @SubscribeEvent
+ public static void onRightClickBlockEvent(PlayerInteractEvent.RightClickBlock event){
+
+ if(event.getEntityPlayer().isSneaking()){
+
+ // The event now has an ItemStack, which greatly simplifies hand-related stuff.
+ ItemStack wand = event.getItemStack();
+
+ if(wand != null && wand.getItem() instanceof ItemWand && WandHelper.getCurrentSpell(wand) instanceof Clairvoyance){
+
+ WizardData properties = WizardData.get(event.getEntityPlayer());
+
+ if(properties != null){
+ // THIS is why BlockPos is a thing - in 1.7.10 this requires a clumsy switch statement.
+ BlockPos pos = event.getPos().offset(event.getFace());
+
+ properties.setClairvoyancePoint(pos, event.getWorld().provider.getDimension());
+
+ if(!event.getWorld().isRemote){
+ event.getEntityPlayer().addChatMessage(new TextComponentTranslation("spell.clairvoyance.confirm", Spells.clairvoyance.getNameForTranslationFormatted()));
+ }
+
+ event.setCanceled(true);
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/java/electroblob/wizardry/spell/CurseOfSoulbinding.java b/src/main/java/electroblob/wizardry/spell/CurseOfSoulbinding.java
index 78c03913..3a2bc261 100644
--- a/src/main/java/electroblob/wizardry/spell/CurseOfSoulbinding.java
+++ b/src/main/java/electroblob/wizardry/spell/CurseOfSoulbinding.java
@@ -6,6 +6,7 @@ import electroblob.wizardry.constants.Element;
import electroblob.wizardry.constants.SpellType;
import electroblob.wizardry.constants.Tier;
import electroblob.wizardry.registry.WizardryItems;
+import electroblob.wizardry.util.IElementalDamage;
import electroblob.wizardry.util.SpellModifiers;
import electroblob.wizardry.util.WizardryParticleType;
import electroblob.wizardry.util.WizardryUtilities;
@@ -17,7 +18,11 @@ import net.minecraft.util.EnumHand;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
+import net.minecraftforge.event.entity.living.LivingHurtEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+@Mod.EventBusSubscriber
public class CurseOfSoulbinding extends Spell {
public CurseOfSoulbinding() {
@@ -53,5 +58,17 @@ public class CurseOfSoulbinding extends Spell {
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.ENTITY_WITHER_SPAWN, 1.0F, world.rand.nextFloat() * 0.2F + 1.0F);
return true;
}
+
+ @SubscribeEvent
+ public static void onLivingHurtEvent(LivingHurtEvent event){
+
+ if(!event.getEntity().worldObj.isRemote && event.getEntityLiving() instanceof EntityPlayer && !event.getSource().isUnblockable()
+ && !(event.getSource() instanceof IElementalDamage && ((IElementalDamage)event.getSource()).isRetaliatory())){
+ WizardData data = WizardData.get((EntityPlayer)event.getEntityLiving());
+ if(data != null){
+ data.damageAllSoulboundCreatures(event.getAmount());
+ }
+ }
+ }
}
diff --git a/src/main/java/electroblob/wizardry/spell/Flight.java b/src/main/java/electroblob/wizardry/spell/Flight.java
index b3d5136e..b4cf6c68 100644
--- a/src/main/java/electroblob/wizardry/spell/Flight.java
+++ b/src/main/java/electroblob/wizardry/spell/Flight.java
@@ -15,7 +15,7 @@ import net.minecraft.world.World;
public class Flight extends Spell {
- public Flight() {
+ public Flight(){
super(Tier.MASTER, 10, Element.EARTH, "flight", SpellType.UTILITY, 0, EnumAction.NONE, true);
}
diff --git a/src/main/java/electroblob/wizardry/spell/Intimidate.java b/src/main/java/electroblob/wizardry/spell/Intimidate.java
index 20e3bbe9..941a9193 100644
--- a/src/main/java/electroblob/wizardry/spell/Intimidate.java
+++ b/src/main/java/electroblob/wizardry/spell/Intimidate.java
@@ -11,6 +11,7 @@ import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.util.SpellModifiers;
import electroblob.wizardry.util.WizardryParticleType;
import electroblob.wizardry.util.WizardryUtilities;
+import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityCreature;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
@@ -24,11 +25,14 @@ import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
+import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+@Mod.EventBusSubscriber
public class Intimidate extends Spell {
- /** The NBT tag name for storing the feared entity's UUID in the target's tag compound. Defined here in case
- * it changes. */
+ /** The NBT tag name for storing the feared entity's UUID in the target's tag compound. */
public static final String NBT_KEY = "fearedEntity";
public Intimidate() {
@@ -101,5 +105,24 @@ public class Intimidate extends Spell {
return false;
}
+
+ @SubscribeEvent
+ public static void onLivingUpdateEvent(LivingUpdateEvent event){
+
+ if(event.getEntityLiving().isPotionActive(WizardryPotions.fear) && event.getEntityLiving() instanceof EntityCreature){
+
+ NBTTagCompound entityNBT = event.getEntityLiving().getEntityData();
+ EntityCreature creature = (EntityCreature)event.getEntityLiving();
+
+ if(entityNBT != null && entityNBT.hasKey(NBT_KEY)){
+
+ Entity caster = WizardryUtilities.getEntityByUUID(creature.worldObj, entityNBT.getUniqueId(NBT_KEY));
+
+ if(caster instanceof EntityLivingBase){
+ Intimidate.runAway(creature, (EntityLivingBase)caster);
+ }
+ }
+ }
+ }
}
diff --git a/src/main/java/electroblob/wizardry/spell/Levitation.java b/src/main/java/electroblob/wizardry/spell/Levitation.java
index 4cff7b9e..cc67b263 100644
--- a/src/main/java/electroblob/wizardry/spell/Levitation.java
+++ b/src/main/java/electroblob/wizardry/spell/Levitation.java
@@ -15,7 +15,7 @@ import net.minecraft.world.World;
public class Levitation extends Spell {
- public Levitation() {
+ public Levitation(){
super(Tier.ADVANCED, 10, Element.SORCERY, "levitation", SpellType.UTILITY, 0, EnumAction.BOW, true);
}
diff --git a/src/main/java/electroblob/wizardry/spell/LightningBolt.java b/src/main/java/electroblob/wizardry/spell/LightningBolt.java
index 9adbc736..ae7e1d28 100644
--- a/src/main/java/electroblob/wizardry/spell/LightningBolt.java
+++ b/src/main/java/electroblob/wizardry/spell/LightningBolt.java
@@ -3,11 +3,14 @@ package electroblob.wizardry.spell;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.constants.SpellType;
import electroblob.wizardry.constants.Tier;
+import electroblob.wizardry.registry.WizardryAchievements;
import electroblob.wizardry.util.SpellModifiers;
import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.effect.EntityLightningBolt;
+import net.minecraft.entity.monster.EntityCreeper;
+import net.minecraft.entity.passive.EntityPig;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction;
import net.minecraft.nbt.NBTTagCompound;
@@ -15,8 +18,15 @@ import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
+import net.minecraftforge.event.entity.EntityStruckByLightningEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+@Mod.EventBusSubscriber
public class LightningBolt extends Spell {
+
+ /** The NBT key used to store the UUID of the player that summoned the lightning bolt. Used for achievements. */
+ public static final String NBT_KEY = "summoningPlayer";
public LightningBolt() {
super(Tier.ADVANCED, 40, Element.LIGHTNING, "lightning_bolt", SpellType.ATTACK, 80, EnumAction.NONE, false);
@@ -46,7 +56,7 @@ public class LightningBolt extends Spell {
// Code for eventhandler recognition; for achievements and such like. Left in for future use.
NBTTagCompound entityNBT = entitylightning.getEntityData();
- entityNBT.setUniqueId("summoningPlayer", caster.getUniqueID());
+ entityNBT.setUniqueId(NBT_KEY, caster.getUniqueID());
}
caster.swingArm(hand);
@@ -87,4 +97,22 @@ public class LightningBolt extends Spell {
public boolean canBeCastByNPCs(){
return true;
}
+
+ @SubscribeEvent
+ public static void onEntityStruckByLightningEvent(EntityStruckByLightningEvent event){
+
+ if(event.getLightning().getEntityData() != null && event.getLightning().getEntityData().hasKey(NBT_KEY)){
+
+ EntityPlayer player = (EntityPlayer)WizardryUtilities.getEntityByUUID(event.getLightning().worldObj, event.getLightning().getEntityData().getUniqueId("summoningPlayer"));
+
+ if(event.getEntity() instanceof EntityCreeper){
+ player.addStat(WizardryAchievements.charge_creeper);
+ }
+
+ if(event.getEntity() instanceof EntityPig){
+ player.addStat(WizardryAchievements.frankenstein);
+ }
+ }
+
+ }
}
diff --git a/src/main/java/electroblob/wizardry/spell/MindControl.java b/src/main/java/electroblob/wizardry/spell/MindControl.java
index 0cb6c6a2..356d58e2 100644
--- a/src/main/java/electroblob/wizardry/spell/MindControl.java
+++ b/src/main/java/electroblob/wizardry/spell/MindControl.java
@@ -6,7 +6,7 @@ import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.constants.SpellType;
import electroblob.wizardry.constants.Tier;
-import electroblob.wizardry.entity.living.EntityWizard;
+import electroblob.wizardry.entity.living.EntityEvilWizard;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.registry.WizardrySounds;
@@ -26,7 +26,12 @@ import net.minecraft.util.EnumHand;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.world.World;
+import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
+import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+@Mod.EventBusSubscriber
public class MindControl extends Spell {
/** The NBT tag name for storing the controlling entity's UUID in the target's tag compound. Defined here in case
@@ -44,10 +49,10 @@ public class MindControl extends Spell {
if(rayTrace != null && rayTrace.entityHit != null && rayTrace.entityHit instanceof EntityLivingBase){
- Entity target = rayTrace.entityHit;
+ EntityLivingBase target = (EntityLivingBase)rayTrace.entityHit;
if(!world.isRemote){
- if(target instanceof EntityPlayer || !target.isNonBoss() || target instanceof INpc){
+ if(!canControl(target)){
// Adds a message saying that the player/boss entity/wizard resisted mind control
if(!world.isRemote) caster.addChatComponentMessage(new TextComponentTranslation("spell.resist", target.getName(), this.getNameForTranslationFormatted()));
@@ -87,8 +92,7 @@ public class MindControl extends Spell {
if(target != null){
if(!world.isRemote){
- if(target instanceof EntityLiving && target.isNonBoss()
- && !(target instanceof EntityWizard)){
+ if(canControl(target)){
if(!MindControl.findMindControlTarget((EntityLiving)target, caster, world)){
// If no valid target was found, this just acts like mind trick.
@@ -118,12 +122,18 @@ public class MindControl extends Spell {
}
return false;
}
-
+
@Override
public boolean canBeCastByNPCs(){
return true;
}
+ /** Returns true if the given entity can be mind controlled (i.e. is not a player, npc, evil wizard or boss). */
+ public static boolean canControl(EntityLivingBase target){
+ return target instanceof EntityLiving && target.isNonBoss() && !(target instanceof INpc)
+ && !(target instanceof EntityEvilWizard);
+ }
+
/**
* Finds the nearest creature to the given target which it is allowed to attack according to the given caster
* and sets it as the target's attack target. Handles both new and old AI and takes follow range into account.
@@ -165,5 +175,42 @@ public class MindControl extends Spell {
}
+ @SubscribeEvent
+ public static void onLivingUpdateEvent(LivingUpdateEvent event){
+ // This was added because something got changed in the AI classes which means LivingSetAttackTargetEvent doesn't
+ // get fired when I want it to... so I'm firing it myself.
+ if(event.getEntityLiving().isPotionActive(WizardryPotions.mind_control) && event.getEntityLiving() instanceof EntityLiving
+ && ((EntityLiving)event.getEntityLiving()).getAttackTarget() != null
+ && !((EntityLiving)event.getEntityLiving()).getAttackTarget().isEntityAlive())
+ ((EntityLiving)event.getEntityLiving()).setAttackTarget(null); // Causes the event to be fired
+ }
-}
+ @SubscribeEvent
+ public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){
+
+ if(event.getEntityLiving().isPotionActive(WizardryPotions.mind_control) && MindControl.canControl(event.getEntityLiving())){
+
+ NBTTagCompound entityNBT = event.getEntityLiving().getEntityData();
+
+ if(entityNBT != null && entityNBT.hasKey(MindControl.NBT_KEY + "Most")){
+
+ Entity caster = WizardryUtilities.getEntityByUUID(event.getEntity().worldObj, entityNBT.getUniqueId(MindControl.NBT_KEY));
+
+ // If the target that the event tried to set is already a valid mind control target, nothing happens.
+ if(event.getTarget() != null && WizardryUtilities.isValidTarget(caster, event.getTarget())) return;
+
+ if(caster instanceof EntityLivingBase){
+
+ if(MindControl.findMindControlTarget((EntityLiving)event.getEntityLiving(), (EntityLivingBase)caster, event.getEntity().worldObj)){
+ // If it worked, skip setting the target to null.
+ return;
+ }
+ }
+ }
+ // If the caster couldn't be found or no valid target was found, this just acts like mind trick.
+ // If the target is null already, no need to set it to null, or infinite loops will occur.
+ if(event.getTarget() != null) ((EntityLiving)event.getEntityLiving()).setAttackTarget(null);
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/electroblob/wizardry/spell/MindTrick.java b/src/main/java/electroblob/wizardry/spell/MindTrick.java
index 2771e47a..d9cab649 100644
--- a/src/main/java/electroblob/wizardry/spell/MindTrick.java
+++ b/src/main/java/electroblob/wizardry/spell/MindTrick.java
@@ -19,7 +19,12 @@ import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.world.World;
+import net.minecraftforge.event.entity.living.LivingAttackEvent;
+import net.minecraftforge.event.entity.living.LivingSetAttackTargetEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+@Mod.EventBusSubscriber
public class MindTrick extends Spell {
public MindTrick() {
@@ -36,13 +41,13 @@ public class MindTrick extends Spell {
EntityLivingBase target = (EntityLivingBase)rayTrace.entityHit;
if(!world.isRemote){
-
+
if(target instanceof EntityPlayer){
-
+
target.addPotionEffect(new PotionEffect(MobEffects.NAUSEA, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0));
-
+
}else if(target instanceof EntityLiving){
-
+
((EntityLiving)target).setAttackTarget(null);
target.addPotionEffect(new PotionEffect(WizardryPotions.mind_trick, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0));
}
@@ -54,7 +59,7 @@ public class MindTrick extends Spell {
0, 0, 0, 0, 0.8f, 0.2f, 1.0f);
}
}
-
+
target.playSound(WizardrySounds.SPELL_DEFLECTION, 0.7F, world.rand.nextFloat() * 0.4F + 0.8F);
caster.swingArm(hand);
return true;
@@ -68,14 +73,14 @@ public class MindTrick extends Spell {
if(target != null){
if(!world.isRemote){
if(target instanceof EntityPlayer){
-
+
target.addPotionEffect(new PotionEffect(MobEffects.NAUSEA, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0));
-
+
}else if(target instanceof EntityLiving){
-
+
((EntityLiving)target).setAttackTarget(null);
target.addPotionEffect(new PotionEffect(WizardryPotions.mind_trick, (int)(300*modifiers.get(WizardryItems.duration_upgrade)), 0));
-
+
}
}else{
for(int i=0; i<10; i++){
@@ -85,16 +90,38 @@ public class MindTrick extends Spell {
0, 0, 0, 0, 0.8f, 0.2f, 1.0f);
}
}
-
+
target.playSound(WizardrySounds.SPELL_DEFLECTION, 0.7F, world.rand.nextFloat() * 0.4F + 0.8F);
caster.swingArm(hand);
return true;
}
return false;
}
-
+
@Override
public boolean canBeCastByNPCs() {
return true;
}
+
+ @SubscribeEvent
+ public static void onLivingAttackEvent(LivingAttackEvent event){
+ if(event.getSource() != null && event.getSource().getEntity() instanceof EntityLivingBase){
+ // Cancels the mind trick effect if the creature takes damage
+ // This has been moved to within an (event.getSource().getEntity() instanceof EntityLivingBase) check so it doesn't
+ // crash the game with a ConcurrentModificationException. If you think about it, mind trick only ought to be
+ // cancelled if something attacks the entity since potions, drowning, cacti etc. don't affect the targeting.
+ if(event.getEntityLiving().isPotionActive(WizardryPotions.mind_trick)){
+ event.getEntityLiving().removePotionEffect(WizardryPotions.mind_trick);
+ }
+ }
+ }
+
+ @SubscribeEvent
+ public static void onLivingSetAttackTargetEvent(LivingSetAttackTargetEvent event){
+ // Mind trick
+ // If the target is null already, no need to set it to null, or infinite loops will occur.
+ if((event.getEntityLiving().isPotionActive(WizardryPotions.mind_trick) || event.getEntityLiving().isPotionActive(WizardryPotions.fear)) && event.getEntityLiving() instanceof EntityLiving && event.getTarget() != null){
+ ((EntityLiving)event.getEntityLiving()).setAttackTarget(null);
+ }
+ }
}
diff --git a/src/main/java/electroblob/wizardry/spell/ShadowWard.java b/src/main/java/electroblob/wizardry/spell/ShadowWard.java
index b521db6e..01132024 100644
--- a/src/main/java/electroblob/wizardry/spell/ShadowWard.java
+++ b/src/main/java/electroblob/wizardry/spell/ShadowWard.java
@@ -3,15 +3,26 @@ package electroblob.wizardry.spell;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.constants.SpellType;
import electroblob.wizardry.constants.Tier;
+import electroblob.wizardry.item.ItemWand;
+import electroblob.wizardry.util.IElementalDamage;
+import electroblob.wizardry.util.MagicDamage;
import electroblob.wizardry.util.SpellModifiers;
+import electroblob.wizardry.util.WandHelper;
import electroblob.wizardry.util.WizardryUtilities;
+import electroblob.wizardry.util.MagicDamage.DamageType;
+import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.SoundEvents;
import net.minecraft.item.EnumAction;
+import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumHand;
import net.minecraft.util.EnumParticleTypes;
import net.minecraft.world.World;
+import net.minecraftforge.event.entity.living.LivingAttackEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+@Mod.EventBusSubscriber
public class ShadowWard extends Spell {
public ShadowWard() {
@@ -20,20 +31,37 @@ public class ShadowWard extends Spell {
@Override
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) {
-
+
if(world.isRemote){
double dx = -1 + 2*world.rand.nextFloat();
double dy = -1 + world.rand.nextFloat();
double dz = -1 + 2*world.rand.nextFloat();
world.spawnParticle(EnumParticleTypes.PORTAL, caster.posX, WizardryUtilities.getPlayerEyesPos(caster), caster.posZ, dx, dy, dz);
}
-
+
if(ticksInUse % 50 == 0){
WizardryUtilities.playSoundAtPlayer(caster, SoundEvents.BLOCK_PORTAL_AMBIENT, 0.6f, 1.5f);
}
-
+
return true;
}
+ @SubscribeEvent
+ public static void onLivingAttackEvent(LivingAttackEvent event){
+ if(event.getSource() != null && event.getSource().getEntity() instanceof EntityLivingBase){
+ // There used to be a check that the target was a player here, but I don't see any reason for it.
+ ItemStack wand = event.getEntityLiving().getActiveItemStack();
+
+ if(wand != null && wand.getItemDamage() < wand.getMaxDamage() && wand.getItem() instanceof ItemWand
+ && WandHelper.getCurrentSpell(wand) instanceof ShadowWard && !event.getSource().isUnblockable()
+ && !(event.getSource() instanceof IElementalDamage && ((IElementalDamage)event.getSource()).isRetaliatory())){
+
+ event.setCanceled(true);
+ // Now we can preserve the original daage source (sort of) as long as we make it retaliatory.
+ event.getEntityLiving().attackEntityFrom(MagicDamage.causeDirectMagicDamage(event.getSource().getEntity(), DamageType.MAGIC, true), event.getAmount()/2);
+ ((EntityLivingBase)event.getSource().getEntity()).attackEntityFrom(MagicDamage.causeDirectMagicDamage(event.getEntityLiving(), DamageType.MAGIC, true), event.getAmount()/2);
+ }
+ }
+ }
}
diff --git a/src/main/java/electroblob/wizardry/spell/Transience.java b/src/main/java/electroblob/wizardry/spell/Transience.java
index 2ad3a0af..96d3f1c7 100644
--- a/src/main/java/electroblob/wizardry/spell/Transience.java
+++ b/src/main/java/electroblob/wizardry/spell/Transience.java
@@ -8,13 +8,19 @@ import electroblob.wizardry.registry.WizardryPotions;
import electroblob.wizardry.registry.WizardrySounds;
import electroblob.wizardry.util.SpellModifiers;
import electroblob.wizardry.util.WizardryUtilities;
+import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.MobEffects;
import net.minecraft.item.EnumAction;
import net.minecraft.potion.PotionEffect;
import net.minecraft.util.EnumHand;
import net.minecraft.world.World;
+import net.minecraftforge.event.entity.living.LivingAttackEvent;
+import net.minecraftforge.event.world.BlockEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+@Mod.EventBusSubscriber
public class Transience extends Spell {
public Transience() {
@@ -28,7 +34,7 @@ public class Transience extends Spell {
@Override
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers) {
-
+
if(!caster.isPotionActive(WizardryPotions.transience)){
if(!world.isRemote){
caster.addPotionEffect(new PotionEffect(WizardryPotions.transience, (int)(400*modifiers.get(WizardryItems.duration_upgrade)), 0));
@@ -40,5 +46,37 @@ public class Transience extends Spell {
return false;
}
+ @SubscribeEvent
+ public static void onLivingAttackEvent(LivingAttackEvent event){
+ if(event.getSource() != null){
+ // Prevents all blockable damage while transience is active
+ if(event.getEntityLiving().isPotionActive(WizardryPotions.transience) && !event.getSource().isUnblockable()){
+ event.setCanceled(true);
+ }
+ // Prevents transient entities from causing any damage
+ if(event.getSource().getEntity() instanceof EntityLivingBase
+ && ((EntityLivingBase)event.getSource().getEntity()).isPotionActive(WizardryPotions.transience)){
+ event.setCanceled(true);
+ }
+ }
+ }
+
+ @SubscribeEvent
+ public static void onBlockPlaceEvent(BlockEvent.PlaceEvent event){
+ // Prevents transient players from placing blocks
+ if(event.getPlayer().isPotionActive(WizardryPotions.transience)){
+ event.setCanceled(true);
+ return;
+ }
+ }
+
+ @SubscribeEvent
+ public static void onBlockBreakEvent(BlockEvent.BreakEvent event){
+ // Prevents transient players from breaking blocks
+ if(event.getPlayer().isPotionActive(WizardryPotions.transience)){
+ event.setCanceled(true);
+ return;
+ }
+ }
}
diff --git a/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java
index fdfa4f89..6e0f4e55 100644
--- a/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java
+++ b/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java
@@ -3,25 +3,36 @@ package electroblob.wizardry.tileentity;
import java.util.HashSet;
import java.util.Set;
+import electroblob.wizardry.WizardData;
import electroblob.wizardry.Wizardry;
+import electroblob.wizardry.constants.Constants;
+import electroblob.wizardry.constants.Tier;
+import electroblob.wizardry.event.SpellBindEvent;
import electroblob.wizardry.item.ItemArcaneTome;
import electroblob.wizardry.item.ItemArmourUpgrade;
import electroblob.wizardry.item.ItemSpellBook;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.item.ItemWizardArmour;
+import electroblob.wizardry.registry.Spells;
+import electroblob.wizardry.registry.WizardryAchievements;
import electroblob.wizardry.registry.WizardryItems;
+import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.WandHelper;
+import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.Container;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.Slot;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.common.MinecraftForge;
public class ContainerArcaneWorkbench extends Container {
- public TileEntityArcaneWorkbench tileEntityArcaneWorkbench;
+ /** The arcane workbench tile entity associated with this container. */
+ public TileEntityArcaneWorkbench tileentity;
public static final ResourceLocation EMPTY_SLOT_CRYSTAL = new ResourceLocation(Wizardry.MODID, "gui/empty_slot_crystal");
public static final ResourceLocation EMPTY_SLOT_UPGRADE = new ResourceLocation(Wizardry.MODID, "gui/empty_slot_upgrade");
@@ -29,6 +40,7 @@ public class ContainerArcaneWorkbench extends Container {
public static final int CRYSTAL_SLOT = 8;
public static final int WAND_SLOT = 9;
public static final int UPGRADE_SLOT = 10;
+
private static final int[][][] SPELL_BOOK_SLOT_COORDS = {
{{80, 22}, {121, 51}, {106, 98}, {54, 98}, {39, 51}, {-999, -999}, {-999, -999}, {-999, -999}},
{{80, 22}, {117, 43}, {117, 85}, {80, 106}, {43, 85}, {43, 43}, {-999, -999}, {-999, -999}},
@@ -38,7 +50,7 @@ public class ContainerArcaneWorkbench extends Container {
public ContainerArcaneWorkbench(IInventory inventory, TileEntityArcaneWorkbench tileentity){
- this.tileEntityArcaneWorkbench = tileentity;
+ this.tileentity = tileentity;
ItemStack wand = tileentity.getStackInSlot(WAND_SLOT);
@@ -47,16 +59,16 @@ public class ContainerArcaneWorkbench extends Container {
}
this.addSlotToContainer(new SlotItemList(tileentity, CRYSTAL_SLOT, 8, 88, 64, WizardryItems.magic_crystal))
- .setBackgroundName(EMPTY_SLOT_CRYSTAL.toString());
-
+ .setBackgroundName(EMPTY_SLOT_CRYSTAL.toString());
+
this.addSlotToContainer(new SlotWandArmour(tileentity, WAND_SLOT, 80, 64, this));
-
- Set- upgrades = new HashSet
- (WandHelper.getSpecialUpgrades());
+
+ Set
- upgrades = new HashSet
- (WandHelper.getSpecialUpgrades()); // Can't be done statically.
upgrades.add(WizardryItems.arcane_tome);
upgrades.add(WizardryItems.armour_upgrade);
-
+
this.addSlotToContainer(new SlotItemList(tileentity, UPGRADE_SLOT, 8, 106, 1, upgrades.toArray(new Item[0])))
- .setBackgroundName(EMPTY_SLOT_UPGRADE.toString());
+ .setBackgroundName(EMPTY_SLOT_UPGRADE.toString());
for(int x = 0; x < 9; x++){
this.addSlotToContainer(new Slot(inventory, x, 8 + x * 18, 196));
@@ -72,17 +84,14 @@ public class ContainerArcaneWorkbench extends Container {
}
@Override
- public boolean canInteractWith(EntityPlayer par1EntityPlayer)
- {
- return this.tileEntityArcaneWorkbench.isUseableByPlayer(par1EntityPlayer);
+ public boolean canInteractWith(EntityPlayer player){
+ return this.tileentity.isUseableByPlayer(player);
}
- /**
- * Called from the central wand/armour slot when its item is changed or removed.
- */
- // I wrote this!
+ /** Called from the central wand/armour slot when its item is changed or removed. */
+ // In case I forget again and think it should have @Override: I wrote this!
public void onSlotChanged(int slotNumber, ItemStack stack, EntityPlayer player){
-
+
if(slotNumber == WAND_SLOT){
if(stack == null || (!(stack.getItem() instanceof ItemWand) && stack.getItem() != WizardryItems.blank_scroll)){
@@ -110,8 +119,8 @@ public class ContainerArcaneWorkbench extends Container {
if(stack.getItem() == WizardryItems.blank_scroll){
// If a blank scroll is added
// The first slot is shown
- this.getSlot(0).xDisplayPosition = ContainerArcaneWorkbench.SPELL_BOOK_SLOT_COORDS[0][0][0];
- this.getSlot(0).yDisplayPosition = ContainerArcaneWorkbench.SPELL_BOOK_SLOT_COORDS[0][0][1];
+ this.getSlot(0).xDisplayPosition = SPELL_BOOK_SLOT_COORDS[0][0][0];
+ this.getSlot(0).yDisplayPosition = SPELL_BOOK_SLOT_COORDS[0][0][1];
// The rest of the slots are hidden
for(int i=1; i inventory
if(clickedSlotId <= UPGRADE_SLOT){
// Tries to move the stack into the player's inventory. If this fails...
- if (!this.mergeItemStack(itemstack, UPGRADE_SLOT + 1, this.inventorySlots.size(), true)){
+ if (!this.mergeItemStack(stack, UPGRADE_SLOT + 1, this.inventorySlots.size(), true)){
return null; // ...nothing else happens.
}
-
- // Inventory -> workbench
- }else{
+ }
+ // Inventory -> workbench
+ else{
// The following logic prevents shift-clicking transferring the items to the wrong slot.
int minSlotId = 0;
int maxSlotId = UPGRADE_SLOT;
- if(itemstack.getItem() instanceof ItemSpellBook){
+ if(stack.getItem() instanceof ItemSpellBook){
minSlotId = 0;
maxSlotId = CRYSTAL_SLOT-1;
}
- else if(itemstack.getItem() == WizardryItems.magic_crystal){
+ else if(stack.getItem() == WizardryItems.magic_crystal){
minSlotId = CRYSTAL_SLOT;
maxSlotId = CRYSTAL_SLOT;
}
- else if(itemstack.getItem() instanceof ItemWand || itemstack.getItem() instanceof ItemWizardArmour
- || itemstack.getItem() == WizardryItems.blank_scroll){
+ else if(stack.getItem() instanceof ItemWand || stack.getItem() instanceof ItemWizardArmour
+ || stack.getItem() == WizardryItems.blank_scroll){
minSlotId = WAND_SLOT;
maxSlotId = WAND_SLOT;
}
- else if(itemstack.getItem() instanceof ItemArcaneTome
- || itemstack.getItem() instanceof ItemArmourUpgrade
- || itemstack.getItem() == WizardryItems.condenser_upgrade
- || itemstack.getItem() == WizardryItems.siphon_upgrade
- || itemstack.getItem() == WizardryItems.range_upgrade
- || itemstack.getItem() == WizardryItems.cooldown_upgrade
- || itemstack.getItem() == WizardryItems.duration_upgrade
- || itemstack.getItem() == WizardryItems.storage_upgrade
- || itemstack.getItem() == WizardryItems.blast_upgrade
- || itemstack.getItem() == WizardryItems.attunement_upgrade){
+ else if(stack.getItem() instanceof ItemArcaneTome
+ || stack.getItem() instanceof ItemArmourUpgrade
+ || WandHelper.isWandUpgrade(stack.getItem())){
minSlotId = UPGRADE_SLOT;
maxSlotId = UPGRADE_SLOT;
}
@@ -222,13 +224,13 @@ public class ContainerArcaneWorkbench extends Container {
return null; // If none of the above cases were true, then the item won't fit in the workbench.
}
- if(!this.mergeItemStack(itemstack, minSlotId, maxSlotId + 1, false))
+ if(!this.mergeItemStack(stack, minSlotId, maxSlotId + 1, false))
{
return null;
}
}
- if (itemstack.stackSize == 0)
+ if (stack.stackSize == 0)
{
slot.putStack((ItemStack)null);
}
@@ -237,12 +239,12 @@ public class ContainerArcaneWorkbench extends Container {
slot.onSlotChanged();
}
- if (itemstack.stackSize == remainder.stackSize)
+ if (stack.stackSize == remainder.stackSize)
{
return null;
}
- slot.onPickupFromSlot(par1EntityPlayer, itemstack);
+ slot.onPickupFromSlot(player, stack);
}
return remainder;
@@ -262,4 +264,198 @@ public class ContainerArcaneWorkbench extends Container {
return false;
}
+ /** Called (via {@link electroblob.wizardry.packet.PacketControlInput PacketControlInput}) when the apply button
+ * in the arcane workbench GUI is pressed. */
+ // All operations on the items contained in the inventory simply call the corresponding methods in the tileentity.
+ // As of 2.1, for the sake of events and neatness of code, this was moved here from TileEntityArcaneWorkbench.
+ public void onApplyButtonPressed(EntityPlayer player){
+
+ ItemStack wand = this.getSlot(WAND_SLOT).getStack();
+ ItemStack[] spellBooks = new ItemStack[CRYSTAL_SLOT];
+ for(int i=0; i 0){
+ for(int i=0; i ((ItemWand)wand.getItem()).tier.level)){
+ spells[i] = Spell.get(spellBooks[i].getItemDamage());
+ }
+ }
+ WandHelper.setSpells(wand, spells);
+
+ // Charges wand by appropriate amount
+ if(crystals != null){
+ int chargeDepleted = wand.getItemDamage();
+ //System.out.println("Charge depleted: " + chargeDepleted);
+ //System.out.println("Crystals found: " + crystals.stackSize);
+ if(crystals.stackSize * Constants.MANA_PER_CRYSTAL < chargeDepleted){
+ //System.out.println("charging");
+ wand.setItemDamage(chargeDepleted - crystals.stackSize * Constants.MANA_PER_CRYSTAL);
+ this.getSlot(CRYSTAL_SLOT).decrStackSize(crystals.stackSize);
+ }else if(chargeDepleted != 0){
+ //System.out.println((int)Math.ceil(((double)chargeDepleted)/50));
+ this.getSlot(CRYSTAL_SLOT).decrStackSize((int)Math.ceil(((double)chargeDepleted)/Constants.MANA_PER_CRYSTAL));
+ wand.setItemDamage(0);
+ }
+ }
+ }
+
+ // Armour
+ else if(wand != null && wand.getItem() instanceof ItemWizardArmour){
+ // Applies legendary upgrade
+ if(upgrade != null && upgrade.getItem() == WizardryItems.armour_upgrade){
+ if(!wand.hasTagCompound()){
+ wand.setTagCompound(new NBTTagCompound());
+ }
+ if(!wand.getTagCompound().hasKey("legendary")){
+ wand.getTagCompound().setBoolean("legendary", true);
+ this.putStackInSlot(UPGRADE_SLOT, null);
+ player.addStat(WizardryAchievements.legendary);
+ }
+ }
+ // Charges armour by appropriate amount
+ if(crystals != null){
+ int chargeDepleted = wand.getItemDamage();
+ if(crystals.stackSize * Constants.MANA_PER_CRYSTAL < chargeDepleted){
+ wand.setItemDamage(chargeDepleted - crystals.stackSize * Constants.MANA_PER_CRYSTAL);
+ this.getSlot(CRYSTAL_SLOT).decrStackSize(crystals.stackSize);
+ }else if(chargeDepleted != 0){
+ this.getSlot(CRYSTAL_SLOT).decrStackSize((int)Math.ceil(((double)chargeDepleted)/Constants.MANA_PER_CRYSTAL));
+ wand.setItemDamage(0);
+ }
+ }
+ }
+
+ // Scrolls
+ else if(wand != null && wand.getItem() == WizardryItems.blank_scroll){
+ // 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(spellBooks[0] != null && (player.capabilities.isCreativeMode || (WizardData.get(player) != null
+ && WizardData.get(player).hasSpellBeenDiscovered(Spell.get(spellBooks[0].getItemDamage()))))
+ && crystals != null && crystals.stackSize * Constants.MANA_PER_CRYSTAL > Spell.get(spellBooks[0].getItemDamage()).cost){
+
+ this.getSlot(CRYSTAL_SLOT).decrStackSize((int)Math.ceil(((double)Spell.get(spellBooks[0].getItemDamage()).cost)/Constants.MANA_PER_CRYSTAL));
+ this.putStackInSlot(WAND_SLOT, new ItemStack(WizardryItems.scroll, 1, spellBooks[0].getItemDamage()));
+ }
+ }
+ }
+
}
diff --git a/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java b/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java
index 65a9f7f1..3a117aaa 100644
--- a/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java
+++ b/src/main/java/electroblob/wizardry/tileentity/TileEntityArcaneWorkbench.java
@@ -3,18 +3,11 @@ package electroblob.wizardry.tileentity;
import java.util.HashSet;
import java.util.Set;
-import electroblob.wizardry.WizardData;
-import electroblob.wizardry.constants.Constants;
-import electroblob.wizardry.constants.Tier;
import electroblob.wizardry.item.ItemWand;
import electroblob.wizardry.item.ItemWizardArmour;
-import electroblob.wizardry.registry.Spells;
-import electroblob.wizardry.registry.WizardryAchievements;
import electroblob.wizardry.registry.WizardryBlocks;
import electroblob.wizardry.registry.WizardryItems;
-import electroblob.wizardry.spell.Spell;
import electroblob.wizardry.util.WandHelper;
-import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.block.Block;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.IInventory;
@@ -286,196 +279,4 @@ public class TileEntityArcaneWorkbench extends TileEntity implements IInventory,
}
}
- /** Called (via {@link electroblob.wizardry.packet.PacketControlInput PacketControlInput}) when the apply button
- * in the arcane workbench GUI is pressed. */
- // IDEA: Perhaps this should be in the container class?
- public void onApplyButtonPressed(EntityPlayer player){
-
- ItemStack wand = this.getStackInSlot(ContainerArcaneWorkbench.WAND_SLOT);
- ItemStack[] spellBooks = new ItemStack[ContainerArcaneWorkbench.CRYSTAL_SLOT];
- for(int i=0; i 0){
- for(int i=0; i ((ItemWand)wand.getItem()).tier.level)){
- spells[i] = Spell.get(spellBooks[i].getItemDamage());
- }
- }
- WandHelper.setSpells(wand, spells);
-
- // Charges wand by appropriate amount
- if(crystals != null){
- int chargeDepleted = wand.getItemDamage();
- //System.out.println("Charge depleted: " + chargeDepleted);
- //System.out.println("Crystals found: " + crystals.stackSize);
- if(crystals.stackSize * Constants.MANA_PER_CRYSTAL < chargeDepleted){
- //System.out.println("charging");
- wand.setItemDamage(chargeDepleted - crystals.stackSize * Constants.MANA_PER_CRYSTAL);
- this.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, crystals.stackSize);
- }else if(chargeDepleted != 0){
- //System.out.println((int)Math.ceil(((double)chargeDepleted)/50));
- this.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, (int)Math.ceil(((double)chargeDepleted)/Constants.MANA_PER_CRYSTAL));
- wand.setItemDamage(0);
- }
- }
-
- // Armour
- }else if(wand != null && wand.getItem() instanceof ItemWizardArmour){
- // Applies legendary upgrade
- if(upgrade != null && upgrade.getItem() == WizardryItems.armour_upgrade){
- if(!wand.hasTagCompound()){
- wand.setTagCompound(new NBTTagCompound());
- }
- if(!wand.getTagCompound().hasKey("legendary")){
- wand.getTagCompound().setBoolean("legendary", true);
- this.setInventorySlotContents(ContainerArcaneWorkbench.UPGRADE_SLOT, null);
- player.addStat(WizardryAchievements.legendary);
- }
- }
- // Charges armour by appropriate amount
- if(crystals != null){
- int chargeDepleted = wand.getItemDamage();
- if(crystals.stackSize * Constants.MANA_PER_CRYSTAL < chargeDepleted){
- wand.setItemDamage(chargeDepleted - crystals.stackSize * Constants.MANA_PER_CRYSTAL);
- this.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, crystals.stackSize);
- }else if(chargeDepleted != 0){
- this.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, (int)Math.ceil(((double)chargeDepleted)/Constants.MANA_PER_CRYSTAL));
- wand.setItemDamage(0);
- }
- }
-
- // Scrolls
- }else if(wand != null && wand.getItem() == WizardryItems.blank_scroll){
- // 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(spellBooks[0] != null && (player.capabilities.isCreativeMode || (WizardData.get(player) != null
- && WizardData.get(player).hasSpellBeenDiscovered(Spell.get(spellBooks[0].getItemDamage()))))
- && crystals != null && crystals.stackSize * Constants.MANA_PER_CRYSTAL > Spell.get(spellBooks[0].getItemDamage()).cost){
-
- this.decrStackSize(ContainerArcaneWorkbench.CRYSTAL_SLOT, (int)Math.ceil(((double)Spell.get(spellBooks[0].getItemDamage()).cost)/Constants.MANA_PER_CRYSTAL));
- this.setInventorySlotContents(ContainerArcaneWorkbench.WAND_SLOT, new ItemStack(WizardryItems.scroll, 1, spellBooks[0].getItemDamage()));
- }
- }
- }
-
}
diff --git a/src/main/java/electroblob/wizardry/util/IElementalDamage.java b/src/main/java/electroblob/wizardry/util/IElementalDamage.java
index 0bfa724b..b66903e5 100644
--- a/src/main/java/electroblob/wizardry/util/IElementalDamage.java
+++ b/src/main/java/electroblob/wizardry/util/IElementalDamage.java
@@ -1,6 +1,12 @@
package electroblob.wizardry.util;
+import electroblob.wizardry.registry.WizardryAchievements;
import electroblob.wizardry.util.MagicDamage.DamageType;
+import net.minecraft.entity.monster.EntityCreeper;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraftforge.event.entity.living.LivingAttackEvent;
+import net.minecraftforge.fml.common.Mod;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
/** This interface allows {@link MagicDamage} and {@link IndirectMagicDamage} to both be treated as instances of a
* single type so that the damage type field can be accessed, rather than having to deal with each of them separately,
@@ -8,10 +14,34 @@ import electroblob.wizardry.util.MagicDamage.DamageType;
* need to extend different subclasses of {@link net.minecraft.util.DamageSource DamageSource}).
* @since Wizardry 1.1
* @author Electroblob */
+@Mod.EventBusSubscriber
public interface IElementalDamage {
-
+
DamageType getType();
-
+
boolean isRetaliatory();
-
+
+ @SubscribeEvent
+ public static void onLivingAttackEvent(LivingAttackEvent event){
+ if(event.getSource() instanceof IElementalDamage){
+ if(MagicDamage.isEntityImmune(((IElementalDamage)event.getSource()).getType(), event.getEntity())){
+ event.setCanceled(true);
+ // I would have liked to have done the 'resist' chat message here, but I overlooked the fact that I
+ // would need an instance of the spell to get its display name!
+ return;
+ }
+ // One convenient side effect of the new damage type system is that I can get rid of all the places where
+ // creepers are charged and just put them here under shock damage - this is precisely the sort of
+ // repetitive code I was trying to get rid of, since errors can (and did!) occur.
+ if(event.getEntityLiving() instanceof EntityCreeper && !((EntityCreeper)event.getEntityLiving()).getPowered()
+ && ((IElementalDamage)event.getSource()).getType() == DamageType.SHOCK){
+ // Charges creepers when they are hit by shock damage
+ WizardryUtilities.chargeCreeper((EntityCreeper)event.getEntityLiving());
+ // Gives the player that caused the shock damage the 'It's Gonna Blow' achievement
+ if(event.getSource().getEntity() instanceof EntityPlayer){
+ ((EntityPlayer)event.getSource().getEntity()).addStat(WizardryAchievements.charge_creeper);
+ }
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/src/main/java/electroblob/wizardry/util/SpellModifiers.java b/src/main/java/electroblob/wizardry/util/SpellModifiers.java
index 39f97c63..996b6fd6 100644
--- a/src/main/java/electroblob/wizardry/util/SpellModifiers.java
+++ b/src/main/java/electroblob/wizardry/util/SpellModifiers.java
@@ -5,22 +5,24 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
+import electroblob.wizardry.event.SpellCastEvent;
import io.netty.buffer.ByteBuf;
import net.minecraft.item.Item;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraftforge.fml.common.network.ByteBufUtils;
/**
- * Object that wraps any number of spell multipliers into one, allowing for expandability within the Spell#cast
+ * Object that wraps any number of spell modifiers into one, allowing for expandability within the Spell#cast
* methods. This class is essentially a glorified {@link Map} which can be written to and read from a {@link ByteBuf}.
- * It is possible to calculate spell multipliers from wand NBT within the cast methods, but this is cumbersome and does
- * not allow the multipliers to be sent to the client, which is sometimes necessary (for example, detonate needs to know
- * about range multipliers on the client side or the particles wouldn't show outside of the base range).
+ * It is possible to calculate spell modifiers from wand NBT within the cast methods, but this is cumbersome and does
+ * not allow the modifiers to be sent to the client, which is sometimes necessary (for example, detonate needs to know
+ * about range modifiers on the client side or the particles wouldn't show outside of the base range).
*
- * Most external interaction with SpellModifiers objects will be in SpellCastEvent.Pre, where you can add additional
- * multipliers to them if desired for use with your own spells, or modify the existing ones. If you have added a wand
+ * Most external interaction with SpellModifiers objects will be in {@link SpellCastEvent.Pre}, where you can add additional
+ * modifiers to them if desired for use with your own spells, or modify the existing ones. If you have added a wand
* upgrade, this is not done automatically for you; you will have to do it yourself (for the simple reason that
- * not all wand upgrades affect spells).
+ * not all wand upgrades affect spells). SpellModifiers objects are mutable, so you can simply change the values
+ * they contain to modify the spell.
*
* To use a SpellModifiers object within the Spell.cast methods, simply retrieve the desired multiplier
* using {@link SpellModifiers#get(Item)} for wand upgrades, or {@link SpellModifiers#get(String)} if the multiplier
@@ -29,13 +31,13 @@ import net.minecraftforge.fml.common.network.ByteBufUtils;
* @since Wizardry 1.2
* @see WandHelper
*/
-// I have made the decision that the USERS of this class must decide whether the multipliers need syncing or not, on a
+// I have made the decision that the USERS of this class must decide whether the modifiers need syncing or not, on a
// case-by-case basis. Why? Because assigning keys to either sync or not sync would be unnecessarily restrictive, and
// would mean they have to be registered, and part of the point of SpellModifiers is that they can be added to on the
// fly.
public final class SpellModifiers {
- /** Constant string identifier for the damage multiplier. All the other multipliers in Wizardry have items. */
+ /** Constant string identifier for the damage modifier. All the other modifiers in Wizardry have items. */
public static final String DAMAGE = "damage";
private Map multiplierMap;
@@ -53,7 +55,7 @@ public final class SpellModifiers {
* upgrade item was registered with.
* @throws IllegalArgumentException if the given item is not a registered special wand upgrade.
* @param upgrade The upgrade item the multiplier corresponds to.
- * @param multiplier The multiplier value, with 1 being default. Usage of multipliers is up to individual spells to
+ * @param multiplier The multiplier value, with 1 being default. Usage of modifiers is up to individual spells to
* implement.
* @param needsSyncing Whether this multiplier should be synchronised with the client via packets. Only set this
* to true if particles will be spawned which need to know the value of the multiplier.
@@ -69,7 +71,7 @@ public final class SpellModifiers {
* multiplier will correspond to a wand upgrade, in which case use {@link SpellModifiers#set(Item, float, boolean)}
* instead.
* @param key The key used to identify the multiplier.
- * @param multiplier The multiplier value, with 1 being default. Usage of multipliers is up to individual spells to
+ * @param multiplier The multiplier value, with 1 being default. Usage of modifiers is up to individual spells to
* implement.
* @param needsSyncing Whether this multiplier should be synchronised with the client via packets. Only set this
* to true if particles will be spawned which depend on the multiplier.
diff --git a/src/main/java/electroblob/wizardry/util/WandHelper.java b/src/main/java/electroblob/wizardry/util/WandHelper.java
index 436f220f..15c0466b 100644
--- a/src/main/java/electroblob/wizardry/util/WandHelper.java
+++ b/src/main/java/electroblob/wizardry/util/WandHelper.java
@@ -23,7 +23,9 @@ import net.minecraft.nbt.NBTTagCompound;
* unlikely case that this is necessary.
*
* Also note that none of the methods in this class actually check that the given ItemStack contains an ItemWand; you
- * can, for example, pass in a stack of snowballs without causing problems, but that is of course pointless!
+ * can, for example, pass in a stack of snowballs without causing problems, but that is of course pointless! However,
+ * if you have your own spell casting item (which doesn't extend ItemWand), this setup means you can still use this class
+ * to manage its NBT structure.
*
* All get methods in this class return some kind of default if the passed-in wand stack has no nbt data.
* See individual method descriptions for more details.
diff --git a/src/main/java/electroblob/wizardry/util/WizardryPathFinder.java b/src/main/java/electroblob/wizardry/util/WizardryPathFinder.java
index 8c5c2d9d..2a431b38 100644
--- a/src/main/java/electroblob/wizardry/util/WizardryPathFinder.java
+++ b/src/main/java/electroblob/wizardry/util/WizardryPathFinder.java
@@ -6,6 +6,7 @@ import javax.annotation.Nullable;
import com.google.common.collect.Sets;
+import electroblob.wizardry.spell.Clairvoyance;
import net.minecraft.entity.EntityLiving;
import net.minecraft.pathfinding.NodeProcessor;
import net.minecraft.pathfinding.Path;
@@ -14,7 +15,8 @@ import net.minecraft.pathfinding.PathPoint;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.IBlockAccess;
-/** Minecraft's pathfinder refused to play nicely, so I 'borrowed' its code and fiddled with it. */
+/** Minecraft's pathfinder refused to play nicely, so I 'borrowed' its code and fiddled with it. Currently this is only
+ * used for the {@link Clairvoyance} spell. */
public class WizardryPathFinder {
/** The path being generated */
diff --git a/src/main/java/electroblob/wizardry/util/WizardryUtilities.java b/src/main/java/electroblob/wizardry/util/WizardryUtilities.java
index c0912b1f..a1cfb659 100644
--- a/src/main/java/electroblob/wizardry/util/WizardryUtilities.java
+++ b/src/main/java/electroblob/wizardry/util/WizardryUtilities.java
@@ -56,6 +56,14 @@ import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
/** This class contains some useful static methods for use anywhere - items, entities, spells, events, blocks, etc.
+ * Broadly speaking, these fall into the following categories:
+ *
+ * - In-world utilities (position calculating, retrieving entities, etc.)
+ * - Raytracing
+ * - Drawing utilities (client-only)
+ * - NBT and data storage utilities
+ * - Interaction with the ally designation system
+ * - Loot and weighting utilities
* @see CommonProxy
* @see WandHelper
* @since Wizardry 1.0