From 75e5de603ef0f658b4ff45f2f272f21eb1139026 Mon Sep 17 00:00:00 2001 From: Electroblob <35599699+Electroblob77@users.noreply.github.com> Date: Sun, 24 Jun 2018 12:52:55 +0100 Subject: [PATCH] Delegate workbench apply button and slot behaviour to item classes --- .../wizardry/item/IWorkbenchItem.java | 56 +++ .../wizardry/item/ItemBlankScroll.java | 56 +++ .../electroblob/wizardry/item/ItemWand.java | 145 ++++++- .../wizardry/item/ItemWizardArmour.java | 53 ++- .../tileentity/ContainerArcaneWorkbench.java | 388 +++++------------- ...WandArmour.java => SlotWorkbenchItem.java} | 15 +- .../tileentity/TileEntityArcaneWorkbench.java | 19 +- 7 files changed, 423 insertions(+), 309 deletions(-) create mode 100644 src/main/java/electroblob/wizardry/item/IWorkbenchItem.java create mode 100644 src/main/java/electroblob/wizardry/item/ItemBlankScroll.java rename src/main/java/electroblob/wizardry/tileentity/{SlotWandArmour.java => SlotWorkbenchItem.java} (59%) diff --git a/src/main/java/electroblob/wizardry/item/IWorkbenchItem.java b/src/main/java/electroblob/wizardry/item/IWorkbenchItem.java new file mode 100644 index 00000000..22af5327 --- /dev/null +++ b/src/main/java/electroblob/wizardry/item/IWorkbenchItem.java @@ -0,0 +1,56 @@ +package electroblob.wizardry.item; + +import electroblob.wizardry.event.SpellBindEvent; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; + +/** + * Items that implement this interface may be placed in the central slot of the arcane workbench as long as + * {@link IWorkbenchItem#canPlace(ItemStack)} returns true.The number of spell book slots displayed is also specified + * using {@link IWorkbenchItem#getSpellSlotCount(ItemStack)}. + *
+ * Items that implement this interface define what happens if they are in the central slot of the arcane workbench and
+ * the apply button is pressed, in {@link IWorkbenchItem#onApplyButtonPressed(EntityPlayer, Slot, Slot, Slot, Slot[])}.
+ * This is a core part of the arcane workbench refactoring in version 4.2 and allows for custom spell casting items and
+ * chargeable armour without requiring that they extend {@link ItemWand} or {@link ItemWizardArmour}.
+ * @author Electroblob
+ * @since Wizardry 4.2
+ */
+public interface IWorkbenchItem {
+
+ /**
+ * Returns true if the item can be placed in the central slot of an arcane workbench, false otherwise. Allows
+ * for itemstack-sensitive behaviour. Returns true by default.
+ * @param stack The stack that is being placed into the workbench.
+ * @return True to allow the item to be placed into the workbench, false to prevent that from happening.
+ */
+ default boolean canPlace(ItemStack stack){
+ return true;
+ }
+
+ /**
+ * Returns the number of spell book slots that should appear in the workbench when this item is placed into it,
+ * based on the given itemstack.
+ * @param stack The stack that is being placed into the workbench.
+ * @return The number of spell book slots that should appear around this item when it is placed into the workbench.
+ * Can be 0, but must not be negative.
+ */
+ int getSpellSlotCount(ItemStack stack);
+
+ /**
+ * Called when this item is in the central slot of an arcane workbench and the apply button is pressed. Items must
+ * implement this method to define what happens when the apply button is pressed. Note that {@link SpellBindEvent}
+ * is fired before this method is called.
+ * @param player The player that pressed the apply button.
+ * @param centre The central slot in the arcane workbench. This slot will always contain a stack of the implementing
+ * item, or in other words, it is guaranteed that {@code this == centre.getStack().getItem()}.
+ * @param crystals The magic crystal slot of the arcane workbench.
+ * @param upgrade The upgrade slot of the arcane workbench.
+ * @param spellBooks An array of the active (visible) spell book slots in the arcane workbench. The length of
+ * the array will be equal to the value returned by {@link IWorkbenchItem#getSpellSlotCount(ItemStack)}.
+ * @return True if anything changed, false if not.
+ */
+ boolean onApplyButtonPressed(EntityPlayer player, Slot centre, Slot crystals, Slot upgrade, Slot[] spellBooks);
+
+}
diff --git a/src/main/java/electroblob/wizardry/item/ItemBlankScroll.java b/src/main/java/electroblob/wizardry/item/ItemBlankScroll.java
new file mode 100644
index 00000000..3e9242d6
--- /dev/null
+++ b/src/main/java/electroblob/wizardry/item/ItemBlankScroll.java
@@ -0,0 +1,56 @@
+package electroblob.wizardry.item;
+
+import electroblob.wizardry.WizardData;
+import electroblob.wizardry.constants.Constants;
+import electroblob.wizardry.registry.Spells;
+import electroblob.wizardry.registry.WizardryItems;
+import electroblob.wizardry.registry.WizardryTabs;
+import electroblob.wizardry.spell.Spell;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.Slot;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+
+public class ItemBlankScroll extends Item implements IWorkbenchItem {
+
+ public ItemBlankScroll(){
+ this.setCreativeTab(WizardryTabs.WIZARDRY);
+ }
+
+ @Override
+ public int getSpellSlotCount(ItemStack stack){
+ return 1;
+ }
+
+ @Override
+ public boolean onApplyButtonPressed(EntityPlayer player, Slot centre, Slot crystals, Slot upgrade, Slot[] spellBooks){
+
+ if(!spellBooks[0].getStack().isEmpty() && !crystals.getStack().isEmpty()){
+
+ Spell spell = Spell.get(spellBooks[0].getStack().getItemDamage());
+ WizardData data = WizardData.get(player);
+
+ // Spells can only be bound to scrolls if the player has already cast them (prevents casting of master
+ // spells without getting a master wand)
+ // This restriction does not apply in creative mode
+ if(spell != Spells.none && player.capabilities.isCreativeMode || (data != null
+ && data.hasSpellBeenDiscovered(spell))){
+
+ int cost = spell.cost;
+ // Continuous spell scrolls require enough mana to cast them for the duration defined in ItemScroll.
+ if(spell.isContinuous) cost *= ItemScroll.CASTING_TIME / 20;
+
+ if(crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL > cost){
+ // Rounds up to the nearest whole crystal
+ crystals.decrStackSize(cost / Constants.MANA_PER_CRYSTAL + 1);
+ centre.putStack(new ItemStack(WizardryItems.scroll, 1, spell.id()));
+ return true;
+ }
+
+ }
+ }
+
+ return false;
+ }
+
+}
diff --git a/src/main/java/electroblob/wizardry/item/ItemWand.java b/src/main/java/electroblob/wizardry/item/ItemWand.java
index 522d819d..bdc1d183 100644
--- a/src/main/java/electroblob/wizardry/item/ItemWand.java
+++ b/src/main/java/electroblob/wizardry/item/ItemWand.java
@@ -13,6 +13,7 @@ import electroblob.wizardry.event.SpellCastEvent;
import electroblob.wizardry.event.SpellCastEvent.Source;
import electroblob.wizardry.packet.PacketCastSpell;
import electroblob.wizardry.packet.WizardryPacketHandler;
+import electroblob.wizardry.registry.Spells;
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryPotions;
@@ -30,6 +31,7 @@ import net.minecraft.entity.EntityLiving;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.EntityEquipmentSlot;
+import net.minecraft.inventory.Slot;
import net.minecraft.item.EnumAction;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
@@ -59,7 +61,10 @@ import net.minecraftforge.fml.relauncher.SideOnly;
*
* @since Wizardry 1.0
*/
-public class ItemWand extends Item {
+public class ItemWand extends Item implements IWorkbenchItem {
+
+ /** The number of spell slots a wand has with no attunement upgrades applied. */
+ public static final int BASE_SPELL_SLOTS = 5;
public Tier tier;
public Element element;
@@ -424,4 +429,142 @@ public class ItemWand extends Item {
return false;
}
+
+ @Override
+ public int getSpellSlotCount(ItemStack stack){
+ return BASE_SPELL_SLOTS + WandHelper.getUpgradeLevel(stack, WizardryItems.attunement_upgrade);
+ }
+
+ @Override
+ public boolean onApplyButtonPressed(EntityPlayer player, Slot centre, Slot crystals, Slot upgrade, Slot[] spellBooks){
+
+ boolean changed = false;
+
+ // Upgrades wand if necessary. Damage is copied, preserving remaining durability,
+ // and also the entire NBT tag compound.
+ if(upgrade.getStack().getItem() == WizardryItems.arcane_tome){
+
+ // Checks the wand upgrade is for the tier above the wand's tier.
+ // It is guaranteed that: this == centre.getStack().getItem()
+ if(upgrade.getStack().getItemDamage() - 1 == this.tier.ordinal()){
+
+ Tier tier = Tier.values()[upgrade.getStack().getItemDamage()];
+
+ ItemStack newWand = new ItemStack(WizardryUtilities.getWand(tier, this.element));
+ newWand.setTagCompound(centre.getStack().getTagCompound());
+ // This needs to be done after copying the tag compound so the max damage for the new wand
+ // takes storage upgrades into account.
+ newWand.setItemDamage(newWand.getMaxDamage() - (centre.getStack().getMaxDamage() - centre.getStack().getItemDamage()));
+
+ centre.putStack(newWand);
+ upgrade.decrStackSize(1);
+
+ if(tier == Tier.APPRENTICE) WizardryAdvancementTriggers.apprentice.triggerFor(player);
+ if(tier == Tier.MASTER) WizardryAdvancementTriggers.master.triggerFor(player);
+
+ changed = true;
+ }
+
+ }else if(WandHelper.isWandUpgrade(upgrade.getStack().getItem())){
+
+ // Special upgrades
+ Item specialUpgrade = upgrade.getStack().getItem();
+
+ if(WandHelper.getTotalUpgrades(centre.getStack()) < this.tier.upgradeLimit
+ && WandHelper.getUpgradeLevel(centre.getStack(), specialUpgrade) < Constants.UPGRADE_STACK_LIMIT){
+
+ // Used to preserve existing mana when upgrading storage rather than creating free mana.
+ int prevMana = centre.getStack().getMaxDamage() - centre.getStack().getItemDamage();
+
+ WandHelper.applyUpgrade(centre.getStack(), specialUpgrade);
+
+ // Special behaviours for specific upgrades
+ if(specialUpgrade == WizardryItems.storage_upgrade){
+
+ centre.getStack().setItemDamage(centre.getStack().getMaxDamage() - prevMana);
+
+ }else if(specialUpgrade == WizardryItems.attunement_upgrade){
+
+ int newSlotCount = BASE_SPELL_SLOTS + WandHelper.getUpgradeLevel(centre.getStack(),
+ WizardryItems.attunement_upgrade);
+
+ Spell[] spells = WandHelper.getSpells(centre.getStack());
+ Spell[] newSpells = new Spell[newSlotCount];
+
+ for(int i = 0; i < newSpells.length; i++){
+ newSpells[i] = i < spells.length && spells[i] != null ? spells[i] : Spells.none;
+ }
+
+ WandHelper.setSpells(centre.getStack(), newSpells);
+
+ int[] cooldowns = WandHelper.getCooldowns(centre.getStack());
+ int[] newCooldowns = new int[newSlotCount];
+
+ if(cooldowns.length > 0){
+ for(int i = 0; i < cooldowns.length; i++){
+ newCooldowns[i] = cooldowns[i];
+ }
+ }
+
+ WandHelper.setCooldowns(centre.getStack(), newCooldowns);
+ }
+
+ upgrade.decrStackSize(1);
+ WizardryAdvancementTriggers.special_upgrade.triggerFor(player);
+
+ if(WandHelper.getTotalUpgrades(centre.getStack()) == Tier.MASTER.upgradeLimit){
+ WizardryAdvancementTriggers.max_out_wand.triggerFor(player);
+ }
+
+ changed = true;
+ }
+ }
+
+ // Reads NBT spell id array to variable, edits this, then writes it back to NBT.
+ // Original spells are preserved; if a slot is left empty the existing spell binding will remain.
+ // Accounts for spells which cannot be applied because they are above the wand's tier; these spells
+ // will not bind but the existing spell in that slot will remain and other applicable spells will
+ // be bound as normal, along with any upgrades and crystals.
+ Spell[] spells = WandHelper.getSpells(centre.getStack());
+
+ if(spells.length <= 0){
+ // Base value here because if the spell array doesn't exist, the wand can't possibly have attunement upgrades
+ spells = new Spell[BASE_SPELL_SLOTS];
+ }
+
+ for(int i = 0; i < spells.length; i++){
+ if(spellBooks[i].getStack() != ItemStack.EMPTY){
+
+ Spell spell = Spell.get(spellBooks[i].getStack().getItemDamage());
+ // If the wand is powerful enough for the spell and it's not already bound to that slot
+ if(!(spell.tier.level > this.tier.level) && spells[i] != spell){
+ spells[i] = spell;
+ changed = true;
+ }
+ }
+ }
+
+ WandHelper.setSpells(centre.getStack(), spells);
+
+ // Charges wand by appropriate amount
+ if(crystals.getStack() != ItemStack.EMPTY){
+
+ int chargeDepleted = centre.getStack().getItemDamage();
+
+ if(crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL < chargeDepleted){
+
+ centre.getStack().setItemDamage(chargeDepleted - crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL);
+ crystals.decrStackSize(crystals.getStack().getCount());
+ changed = true;
+
+ }else if(chargeDepleted != 0){
+
+ centre.getStack().setItemDamage(0);
+ crystals.decrStackSize((int)Math.ceil(((double)chargeDepleted) / Constants.MANA_PER_CRYSTAL));
+ changed = true;
+ }
+ }
+
+ return changed;
+ }
}
diff --git a/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java b/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java
index bb356ec8..898576b5 100644
--- a/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java
+++ b/src/main/java/electroblob/wizardry/item/ItemWizardArmour.java
@@ -11,6 +11,7 @@ import electroblob.wizardry.block.BlockStatue;
import electroblob.wizardry.constants.Constants;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.registry.WizardryAdvancementTriggers;
+import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.registry.WizardryTabs;
import net.minecraft.client.model.ModelBiped;
import net.minecraft.client.util.ITooltipFlag;
@@ -20,9 +21,11 @@ import net.minecraft.entity.SharedMonsterAttributes;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.inventory.EntityEquipmentSlot;
+import net.minecraft.inventory.Slot;
import net.minecraft.item.EnumAction;
import net.minecraft.item.ItemArmor;
import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.util.EnumHandSide;
import net.minecraft.world.World;
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
@@ -32,7 +35,7 @@ import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
@Mod.EventBusSubscriber
-public class ItemWizardArmour extends ItemArmor {
+public class ItemWizardArmour extends ItemArmor implements IWorkbenchItem {
//VanillaCopy, ItemArmor has this set to private for some reason.
public static final UUID[] ARMOR_MODIFIERS = new UUID[] {UUID.fromString("845DB27C-C624-495F-8C9F-6020A9A58B6B"), UUID.fromString("D8499B04-0E66-4726-AB29-64469D734E0D"), UUID.fromString("9F3D476D-C118-4544-8365-64846904B48E"), UUID.fromString("2AD3F246-FEE1-4E67-B886-69FD380BB150")};
@@ -211,4 +214,52 @@ public class ItemWizardArmour extends ItemArmor {
}
}
+ @Override
+ public int getSpellSlotCount(ItemStack stack){
+ return 0; // Doesn't have any spell slots!
+ }
+
+ @Override
+ public boolean onApplyButtonPressed(EntityPlayer player, Slot centre, Slot crystals, Slot upgrade, Slot[] spellBooks){
+
+ boolean changed = false;
+
+ // Applies legendary upgrade
+ if(upgrade.getStack().getItem() == WizardryItems.armour_upgrade){
+
+ if(!centre.getStack().hasTagCompound()){
+ centre.getStack().setTagCompound(new NBTTagCompound());
+ }
+
+ if(!centre.getStack().getTagCompound().hasKey("legendary")){
+
+ centre.getStack().getTagCompound().setBoolean("legendary", true);
+ upgrade.decrStackSize(1);
+ WizardryAdvancementTriggers.legendary.triggerFor(player);
+ changed = true;
+ }
+ }
+
+ // Charges armour by appropriate amount
+ if(crystals.getStack() != ItemStack.EMPTY){
+
+ int chargeDepleted = centre.getStack().getItemDamage();
+
+ if(crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL < chargeDepleted){
+
+ centre.getStack().setItemDamage(chargeDepleted - crystals.getStack().getCount() * Constants.MANA_PER_CRYSTAL);
+ crystals.decrStackSize(crystals.getStack().getCount());
+ changed = true;
+
+ }else if(chargeDepleted != 0){
+
+ centre.getStack().setItemDamage(0);
+ crystals.decrStackSize((int)Math.ceil(((double)chargeDepleted) / Constants.MANA_PER_CRYSTAL));
+ changed = true;
+ }
+ }
+
+ return changed;
+ }
+
}
diff --git a/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java b/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java
index 8615c69f..523f9b7a 100644
--- a/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java
+++ b/src/main/java/electroblob/wizardry/tileentity/ContainerArcaneWorkbench.java
@@ -3,30 +3,22 @@ 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.IWorkbenchItem;
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.WizardryAdvancementTriggers;
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.minecraft.util.math.MathHelper;
import net.minecraftforge.common.MinecraftForge;
public class ContainerArcaneWorkbench extends Container {
@@ -40,29 +32,26 @@ public class ContainerArcaneWorkbench extends Container {
"gui/empty_slot_upgrade");
public static final int CRYSTAL_SLOT = 8;
- public static final int WAND_SLOT = 9;
+ public static final int CENTRE_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}},
- {{80, 22}, {113, 38}, {121, 74}, {98, 102}, {62, 102}, {39, 74}, {47, 38}, {-999, -999}},
- {{80, 22}, {111, 33}, {122, 64}, {111, 95}, {80, 106}, {49, 95}, {38, 64}, {49, 33}}};
+
+ public static final int SLOT_RADIUS = 42;
public ContainerArcaneWorkbench(IInventory inventory, TileEntityArcaneWorkbench tileentity){
this.tileentity = tileentity;
- ItemStack wand = tileentity.getStackInSlot(WAND_SLOT);
+ ItemStack wand = tileentity.getStackInSlot(CENTRE_SLOT);
for(int i = 0; i < 8; i++){
- this.addSlotToContainer(new SlotItemList(tileentity, i, -999, -999, 1, WizardryItems.spell_book));
+ Slot slot = new SlotItemList(tileentity, i, -999, -999, 1, WizardryItems.spell_book);
+ this.addSlotToContainer(slot);
}
this.addSlotToContainer(new SlotItemList(tileentity, CRYSTAL_SLOT, 8, 88, 64, WizardryItems.magic_crystal))
.setBackgroundName(EMPTY_SLOT_CRYSTAL.toString());
- this.addSlotToContainer(new SlotWandArmour(tileentity, WAND_SLOT, 80, 64, this));
+ this.addSlotToContainer(new SlotWorkbenchItem(tileentity, CENTRE_SLOT, 80, 64, this));
Set