467 lines
18 KiB
Java
467 lines
18 KiB
Java
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 {
|
|
|
|
/** 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");
|
|
|
|
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}},
|
|
{{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 ContainerArcaneWorkbench(IInventory inventory, TileEntityArcaneWorkbench tileentity){
|
|
|
|
this.tileentity = tileentity;
|
|
|
|
ItemStack wand = tileentity.getStackInSlot(WAND_SLOT);
|
|
|
|
for(int i = 0; i < 8; i++){
|
|
this.addSlotToContainer(new SlotItemList(tileentity, i, -999, -999, 1, WizardryItems.spell_book));
|
|
}
|
|
|
|
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));
|
|
|
|
Set<Item> upgrades = new HashSet<Item>(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());
|
|
|
|
for(int x = 0; x < 9; x++){
|
|
this.addSlotToContainer(new Slot(inventory, x, 8 + x * 18, 196));
|
|
}
|
|
|
|
for(int y = 0; y < 3; y++){
|
|
for(int x = 0; x < 9; x++){
|
|
this.addSlotToContainer(new Slot(inventory, 9 + x + y * 9, 8 + x * 18, 138 + y * 18));
|
|
}
|
|
}
|
|
|
|
this.onSlotChanged(WAND_SLOT, wand, null);
|
|
}
|
|
|
|
@Override
|
|
public boolean canInteractWith(EntityPlayer player){
|
|
return this.tileentity.isUsableByPlayer(player);
|
|
}
|
|
|
|
/** 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.getItem() instanceof ItemWand) && stack.getItem() != WizardryItems.blank_scroll){
|
|
// If the stack has been removed
|
|
for(int i = 0; i < CRYSTAL_SLOT; i++){
|
|
Slot slot1 = this.getSlot(i);
|
|
// 'Removes' the slot from the container (moves it off the screen)
|
|
slot1.xPos = -100;
|
|
slot1.yPos = -100;
|
|
|
|
ItemStack stack1 = slot1.getStack();
|
|
// This doesn't cause an infinite loop because slot i can never be a SlotWandArmour. In effect, it's
|
|
// exactly the same as shift-clicking the slot, so why re-invent the wheel?
|
|
ItemStack remainder = this.transferStackInSlot(player, i);
|
|
|
|
if(remainder == ItemStack.EMPTY && stack1 != ItemStack.EMPTY){
|
|
slot1.putStack(ItemStack.EMPTY);
|
|
// The second parameter is never used...
|
|
if(player != null) player.dropItem(stack1, false);
|
|
}
|
|
}
|
|
|
|
}else{
|
|
|
|
if(stack.getItem() == WizardryItems.blank_scroll){
|
|
// If a blank scroll is added
|
|
// The first slot is shown
|
|
this.getSlot(0).xPos = SPELL_BOOK_SLOT_COORDS[0][0][0];
|
|
this.getSlot(0).yPos = SPELL_BOOK_SLOT_COORDS[0][0][1];
|
|
|
|
// The rest of the slots are hidden
|
|
for(int i = 1; i < CRYSTAL_SLOT; i++){
|
|
|
|
Slot slot1 = this.getSlot(i);
|
|
|
|
slot1.xPos = -100;
|
|
slot1.yPos = -100;
|
|
|
|
ItemStack stack1 = slot1.getStack();
|
|
// This doesn't cause an infinite loop because slot i can never be a SlotWandArmour. In effect,
|
|
// it's
|
|
// exactly the same as shift-clicking the slot, so why re-invent the wheel?
|
|
ItemStack remainder = this.transferStackInSlot(player, i);
|
|
|
|
if(remainder == ItemStack.EMPTY && stack1 != ItemStack.EMPTY){
|
|
slot1.putStack(ItemStack.EMPTY);
|
|
// The second parameter is never used...
|
|
if(player != null) player.dropItem(stack1, false);
|
|
}
|
|
}
|
|
|
|
}else{
|
|
|
|
for(int i = 0; i < CRYSTAL_SLOT; i++){
|
|
|
|
int n = WandHelper.getUpgradeLevel(stack, WizardryItems.attunement_upgrade);
|
|
int[] coords = SPELL_BOOK_SLOT_COORDS[n][i];
|
|
|
|
Slot slot1 = this.getSlot(i);
|
|
// Puts the slot back in the correct position
|
|
slot1.xPos = coords[0];
|
|
slot1.yPos = coords[1];
|
|
|
|
if(slot1.xPos < 0 || slot1.yPos < 0){
|
|
|
|
ItemStack stack1 = slot1.getStack();
|
|
// This doesn't cause an infinite loop because slot i can never be a SlotWandArmour. In
|
|
// effect, it's
|
|
// exactly the same as shift-clicking the slot, so why re-invent the wheel?
|
|
ItemStack remainder = this.transferStackInSlot(player, i);
|
|
|
|
if(remainder == ItemStack.EMPTY && stack1 != ItemStack.EMPTY){
|
|
slot1.putStack(ItemStack.EMPTY);
|
|
// The second parameter is never used...
|
|
if(player != null) player.dropItem(stack1, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// FIXME: It only seems to be syncing correctly when a stack is put into the slot, not taken out.
|
|
// Was this broken in 1.7.10 as well?
|
|
this.tileentity.sync();
|
|
}
|
|
|
|
@Override
|
|
public ItemStack transferStackInSlot(EntityPlayer player, int clickedSlotId){
|
|
|
|
ItemStack remainder = ItemStack.EMPTY;
|
|
Slot slot = (Slot)this.inventorySlots.get(clickedSlotId);
|
|
|
|
if(slot != null && slot.getHasStack()){
|
|
ItemStack stack = slot.getStack(); // The stack that was there originally
|
|
remainder = stack.copy(); // A copy of that stack
|
|
|
|
// Workbench -> inventory
|
|
if(clickedSlotId <= UPGRADE_SLOT){
|
|
// Tries to move the stack into the player's inventory. If this fails...
|
|
if(!this.mergeItemStack(stack, UPGRADE_SLOT + 1, this.inventorySlots.size(), true)){
|
|
return ItemStack.EMPTY; // ...nothing else happens.
|
|
}
|
|
}
|
|
// Inventory -> workbench
|
|
else{
|
|
// The following logic prevents shift-clicking transferring the items to the wrong slot.
|
|
int minSlotId = 0;
|
|
int maxSlotId = UPGRADE_SLOT;
|
|
|
|
if(stack.getItem() instanceof ItemSpellBook){
|
|
minSlotId = 0;
|
|
maxSlotId = CRYSTAL_SLOT - 1;
|
|
}else if(stack.getItem() == WizardryItems.magic_crystal){
|
|
minSlotId = CRYSTAL_SLOT;
|
|
maxSlotId = CRYSTAL_SLOT;
|
|
}else if(stack.getItem() instanceof ItemWand || stack.getItem() instanceof ItemWizardArmour
|
|
|| stack.getItem() == WizardryItems.blank_scroll){
|
|
minSlotId = WAND_SLOT;
|
|
maxSlotId = WAND_SLOT;
|
|
}else if(stack.getItem() instanceof ItemArcaneTome || stack.getItem() instanceof ItemArmourUpgrade
|
|
|| WandHelper.isWandUpgrade(stack.getItem())){
|
|
minSlotId = UPGRADE_SLOT;
|
|
maxSlotId = UPGRADE_SLOT;
|
|
}else{
|
|
return ItemStack.EMPTY; // If none of the above cases were true, then the item won't fit in the
|
|
// workbench.
|
|
}
|
|
|
|
if(!this.mergeItemStack(stack, minSlotId, maxSlotId + 1, false)){
|
|
return ItemStack.EMPTY;
|
|
}
|
|
}
|
|
|
|
if(stack.getCount() == 0){
|
|
slot.putStack(ItemStack.EMPTY);
|
|
}else{
|
|
slot.onSlotChanged();
|
|
}
|
|
|
|
if(stack.getCount() == remainder.getCount()){
|
|
return ItemStack.EMPTY;
|
|
}
|
|
|
|
slot.onTake(player, stack);
|
|
}
|
|
|
|
return remainder;
|
|
}
|
|
|
|
// Overridden to stop stacks merging into 'removed' slots.
|
|
@Override
|
|
protected boolean mergeItemStack(ItemStack stack, int minSlotID, int maxSlotID, boolean p_75135_4_){
|
|
|
|
for(int i = minSlotID; i < maxSlotID; i++){
|
|
// System.out.println(this.getSlot(i).xDisplayPosition);
|
|
if(this.getSlot(i).xPos >= 0 && this.getSlot(i).yPos >= 0 && !this.getSlot(i).getHasStack()){
|
|
return super.mergeItemStack(stack, minSlotID, maxSlotID, p_75135_4_);
|
|
}
|
|
}
|
|
// Only returns false if none of the slots given are enabled and empty
|
|
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 < spellBooks.length; i++){
|
|
spellBooks[i] = this.getSlot(i).getStack();
|
|
}
|
|
ItemStack crystals = this.getSlot(CRYSTAL_SLOT).getStack();
|
|
ItemStack upgrade = this.getSlot(UPGRADE_SLOT).getStack();
|
|
|
|
if(MinecraftForge.EVENT_BUS.post(new SpellBindEvent(player, this))) return;
|
|
|
|
// Since the workbench now accepts armour as well as wands, this check is needed.
|
|
if(wand.getItem() instanceof ItemWand){
|
|
|
|
// Upgrades wand if necessary. Damage is copied, preserving remaining durability,
|
|
// and also the entire NBT tag compound.
|
|
if(upgrade.getItem() == WizardryItems.arcane_tome){
|
|
|
|
ItemStack newWand;
|
|
|
|
switch(Tier.values()[upgrade.getItemDamage()]){
|
|
|
|
case APPRENTICE:
|
|
if(((ItemWand)wand.getItem()).tier == Tier.BASIC){
|
|
newWand = new ItemStack(WizardryUtilities.getWand(Tier.values()[upgrade.getItemDamage()],
|
|
((ItemWand)wand.getItem()).element));
|
|
newWand.setTagCompound(wand.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() - (wand.getMaxDamage() - wand.getItemDamage()));
|
|
this.putStackInSlot(WAND_SLOT, newWand);
|
|
this.putStackInSlot(UPGRADE_SLOT, ItemStack.EMPTY);
|
|
player.addStat(WizardryAchievements.apprentice, 1);
|
|
}
|
|
break;
|
|
|
|
case ADVANCED:
|
|
if(((ItemWand)wand.getItem()).tier == Tier.APPRENTICE){
|
|
newWand = new ItemStack(WizardryUtilities.getWand(Tier.values()[upgrade.getItemDamage()],
|
|
((ItemWand)wand.getItem()).element));
|
|
newWand.setTagCompound(wand.getTagCompound());
|
|
newWand.setItemDamage(newWand.getMaxDamage() - (wand.getMaxDamage() - wand.getItemDamage()));
|
|
this.putStackInSlot(WAND_SLOT, newWand);
|
|
this.putStackInSlot(UPGRADE_SLOT, ItemStack.EMPTY);
|
|
}
|
|
break;
|
|
|
|
case MASTER:
|
|
if(((ItemWand)wand.getItem()).tier == Tier.ADVANCED){
|
|
newWand = new ItemStack(WizardryUtilities.getWand(Tier.values()[upgrade.getItemDamage()],
|
|
((ItemWand)wand.getItem()).element));
|
|
newWand.setTagCompound(wand.getTagCompound());
|
|
newWand.setItemDamage(newWand.getMaxDamage() - (wand.getMaxDamage() - wand.getItemDamage()));
|
|
this.putStackInSlot(WAND_SLOT, newWand);
|
|
this.putStackInSlot(UPGRADE_SLOT, ItemStack.EMPTY);
|
|
player.addStat(WizardryAchievements.master, 1);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// This needs to happen so the charging works on the new wand, not the old one.
|
|
wand = this.getSlot(WAND_SLOT).getStack();
|
|
|
|
}else{
|
|
|
|
// Special upgrades
|
|
|
|
// Used to preserve existing mana when upgrading storage rather than creating free mana.
|
|
int prevMana = wand.getMaxDamage() - wand.getItemDamage();
|
|
|
|
if(WandHelper.getTotalUpgrades(wand) < ((ItemWand)wand.getItem()).tier.upgradeLimit
|
|
&& WandHelper.getUpgradeLevel(wand, upgrade.getItem()) < Constants.UPGRADE_STACK_LIMIT){
|
|
|
|
WandHelper.applyUpgrade(wand, upgrade.getItem());
|
|
|
|
// Special behaviours for specific upgrades
|
|
if(upgrade.getItem() == WizardryItems.storage_upgrade){
|
|
wand.setItemDamage(wand.getMaxDamage() - prevMana);
|
|
}
|
|
if(upgrade.getItem() == WizardryItems.attunement_upgrade){
|
|
|
|
Spell[] spells = WandHelper.getSpells(wand);
|
|
Spell[] newSpells = new Spell[5
|
|
+ WandHelper.getUpgradeLevel(wand, WizardryItems.attunement_upgrade)];
|
|
|
|
for(int i = 0; i < newSpells.length; i++){
|
|
// Prevents both NPEs and AIOOBEs
|
|
newSpells[i] = i < spells.length && spells[i] != null ? spells[i] : Spells.none;
|
|
}
|
|
|
|
WandHelper.setSpells(wand, newSpells);
|
|
|
|
int[] cooldown = WandHelper.getCooldowns(wand);
|
|
int[] newCooldown = new int[5
|
|
+ WandHelper.getUpgradeLevel(wand, WizardryItems.attunement_upgrade)];
|
|
|
|
if(cooldown.length > 0){
|
|
for(int i = 0; i < cooldown.length; i++){
|
|
newCooldown[i] = cooldown[i];
|
|
}
|
|
}
|
|
|
|
WandHelper.setCooldowns(wand, newCooldown);
|
|
}
|
|
|
|
this.getSlot(UPGRADE_SLOT).decrStackSize(1);
|
|
player.addStat(WizardryAchievements.special_upgrade, 1);
|
|
|
|
if(WandHelper.getTotalUpgrades(wand) == Tier.MASTER.upgradeLimit){
|
|
player.addStat(WizardryAchievements.max_out_wand, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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(wand);
|
|
if(spells.length <= 0){
|
|
// 5 here because if the spell array doesn't exist, the wand can't possibly have attunement upgrades
|
|
spells = new Spell[5];
|
|
}
|
|
for(int i = 0; i < spells.length; i++){
|
|
if(spellBooks[i] != ItemStack.EMPTY && !(Spell
|
|
.get(spellBooks[i].getItemDamage()).tier.level > ((ItemWand)wand.getItem()).tier.level)){
|
|
spells[i] = Spell.get(spellBooks[i].getItemDamage());
|
|
}
|
|
}
|
|
WandHelper.setSpells(wand, spells);
|
|
|
|
// Charges wand by appropriate amount
|
|
if(crystals != ItemStack.EMPTY){
|
|
int chargeDepleted = wand.getItemDamage();
|
|
// System.out.println("Charge depleted: " + chargeDepleted);
|
|
// System.out.println("Crystals found: " + crystals.getCount());
|
|
if(crystals.getCount() * Constants.MANA_PER_CRYSTAL < chargeDepleted){
|
|
// System.out.println("charging");
|
|
wand.setItemDamage(chargeDepleted - crystals.getCount() * Constants.MANA_PER_CRYSTAL);
|
|
this.getSlot(CRYSTAL_SLOT).decrStackSize(crystals.getCount());
|
|
}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.getItem() instanceof ItemWizardArmour){
|
|
// Applies legendary upgrade
|
|
if(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, ItemStack.EMPTY);
|
|
player.addStat(WizardryAchievements.legendary);
|
|
}
|
|
}
|
|
// Charges armour by appropriate amount
|
|
if(crystals != ItemStack.EMPTY){
|
|
int chargeDepleted = wand.getItemDamage();
|
|
if(crystals.getCount() * Constants.MANA_PER_CRYSTAL < chargeDepleted){
|
|
wand.setItemDamage(chargeDepleted - crystals.getCount() * Constants.MANA_PER_CRYSTAL);
|
|
this.getSlot(CRYSTAL_SLOT).decrStackSize(crystals.getCount());
|
|
}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.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] != ItemStack.EMPTY
|
|
&& (player.capabilities.isCreativeMode || (WizardData.get(player) != null
|
|
&& WizardData.get(player).hasSpellBeenDiscovered(Spell.get(spellBooks[0].getItemDamage()))))
|
|
&& crystals != ItemStack.EMPTY && crystals.getCount()
|
|
* 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()));
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|