From d53c0410780e64e29e3d62bea0468469bc4af7f6 Mon Sep 17 00:00:00 2001 From: Jeremy Diamond Date: Thu, 11 Sep 2014 04:28:05 -0700 Subject: [PATCH] WOOOOOOORDS --- src/api/java/thaumcraft/api/IGoggles.java | 22 + src/api/java/thaumcraft/api/IRepairable.java | 13 + .../thaumcraft/api/IRepairableExtended.java | 17 + src/api/java/thaumcraft/api/IRunicArmor.java | 22 + src/api/java/thaumcraft/api/IScribeTools.java | 14 + .../java/thaumcraft/api/IVisDiscountGear.java | 20 + src/api/java/thaumcraft/api/ItemApi.java | 70 ++ src/api/java/thaumcraft/api/ItemRunic.java | 21 + .../java/thaumcraft/api/ThaumcraftApi.java | 536 ++++++++++++++ .../thaumcraft/api/ThaumcraftApiHelper.java | 293 ++++++++ .../java/thaumcraft/api/TileThaumcraft.java | 63 ++ .../java/thaumcraft/api/WorldCoordinates.java | 117 +++ .../java/thaumcraft/api/aspects/Aspect.java | 201 ++++++ .../thaumcraft/api/aspects/AspectList.java | 256 +++++++ .../api/aspects/AspectSourceHelper.java | 58 ++ .../api/aspects/IAspectContainer.java | 80 +++ .../thaumcraft/api/aspects/IAspectSource.java | 16 + .../api/aspects/IEssentiaContainerItem.java | 37 + .../api/aspects/IEssentiaTransport.java | 100 +++ .../api/crafting/CrucibleRecipe.java | 103 +++ .../api/crafting/IArcaneRecipe.java | 35 + .../api/crafting/IInfusionStabiliser.java | 19 + .../crafting/InfusionEnchantmentRecipe.java | 156 ++++ .../api/crafting/InfusionRecipe.java | 128 ++++ .../api/crafting/ShapedArcaneRecipe.java | 261 +++++++ .../api/crafting/ShapelessArcaneRecipe.java | 157 ++++ .../DamageSourceIndirectThaumcraftEntity.java | 32 + .../damagesource/DamageSourceThaumcraft.java | 47 ++ .../thaumcraft/api/entities/ITaintedMob.java | 5 + src/api/java/thaumcraft/api/nodes/INode.java | 53 ++ .../java/thaumcraft/api/nodes/IRevealer.java | 22 + .../thaumcraft/api/nodes/NodeModifier.java | 6 + .../java/thaumcraft/api/nodes/NodeType.java | 6 + src/api/java/thaumcraft/api/package-info.java | 4 + .../api/potions/PotionFluxTaint.java | 67 ++ .../api/potions/PotionVisExhaust.java | 48 ++ .../api/research/IScanEventHandler.java | 9 + .../api/research/ResearchCategories.java | 101 +++ .../api/research/ResearchCategoryList.java | 37 + .../thaumcraft/api/research/ResearchItem.java | 367 ++++++++++ .../thaumcraft/api/research/ResearchPage.java | 193 +++++ .../thaumcraft/api/research/ScanResult.java | 39 + .../thaumcraft/api/visnet/TileVisNode.java | 188 +++++ .../thaumcraft/api/visnet/VisNetHandler.java | 284 ++++++++ .../java/thaumcraft/api/wands/IWandFocus.java | 64 ++ .../api/wands/IWandRodOnUpdate.java | 16 + .../api/wands/IWandTriggerManager.java | 15 + .../java/thaumcraft/api/wands/IWandable.java | 25 + .../thaumcraft/api/wands/ItemFocusBasic.java | 166 +++++ .../java/thaumcraft/api/wands/StaffRod.java | 48 ++ .../java/thaumcraft/api/wands/WandCap.java | 129 ++++ .../java/thaumcraft/api/wands/WandRod.java | 158 ++++ .../api/wands/WandTriggerRegistry.java | 126 ++++ src/api/java/truetyper/FontHelper.java | 122 ++++ src/api/java/truetyper/FontLoader.java | 63 ++ src/api/java/truetyper/Formatter.java | 86 +++ src/api/java/truetyper/LICENSE.md | 674 ++++++++++++++++++ src/api/java/truetyper/README.md | 51 ++ src/api/java/truetyper/TrueTypeFont.java | 496 +++++++++++++ .../java/fox/spiteful/forbidden/Config.java | 5 - .../spiteful/forbidden/compat/BloodMagic.java | 4 +- .../fox/spiteful/forbidden/compat/Compat.java | 8 +- .../forbidden/items/ItemTaintShovel.java | 40 +- .../items/wands/TaintedWandUpdate.java | 2 +- .../items/wands/YandereWandUpdate.java | 2 +- .../assets/forbidden/lang/en_US.lang | 8 +- 66 files changed, 6603 insertions(+), 28 deletions(-) create mode 100644 src/api/java/thaumcraft/api/IGoggles.java create mode 100644 src/api/java/thaumcraft/api/IRepairable.java create mode 100644 src/api/java/thaumcraft/api/IRepairableExtended.java create mode 100644 src/api/java/thaumcraft/api/IRunicArmor.java create mode 100644 src/api/java/thaumcraft/api/IScribeTools.java create mode 100644 src/api/java/thaumcraft/api/IVisDiscountGear.java create mode 100644 src/api/java/thaumcraft/api/ItemApi.java create mode 100644 src/api/java/thaumcraft/api/ItemRunic.java create mode 100644 src/api/java/thaumcraft/api/ThaumcraftApi.java create mode 100644 src/api/java/thaumcraft/api/ThaumcraftApiHelper.java create mode 100644 src/api/java/thaumcraft/api/TileThaumcraft.java create mode 100644 src/api/java/thaumcraft/api/WorldCoordinates.java create mode 100644 src/api/java/thaumcraft/api/aspects/Aspect.java create mode 100644 src/api/java/thaumcraft/api/aspects/AspectList.java create mode 100644 src/api/java/thaumcraft/api/aspects/AspectSourceHelper.java create mode 100644 src/api/java/thaumcraft/api/aspects/IAspectContainer.java create mode 100644 src/api/java/thaumcraft/api/aspects/IAspectSource.java create mode 100644 src/api/java/thaumcraft/api/aspects/IEssentiaContainerItem.java create mode 100644 src/api/java/thaumcraft/api/aspects/IEssentiaTransport.java create mode 100644 src/api/java/thaumcraft/api/crafting/CrucibleRecipe.java create mode 100644 src/api/java/thaumcraft/api/crafting/IArcaneRecipe.java create mode 100644 src/api/java/thaumcraft/api/crafting/IInfusionStabiliser.java create mode 100644 src/api/java/thaumcraft/api/crafting/InfusionEnchantmentRecipe.java create mode 100644 src/api/java/thaumcraft/api/crafting/InfusionRecipe.java create mode 100644 src/api/java/thaumcraft/api/crafting/ShapedArcaneRecipe.java create mode 100644 src/api/java/thaumcraft/api/crafting/ShapelessArcaneRecipe.java create mode 100644 src/api/java/thaumcraft/api/damagesource/DamageSourceIndirectThaumcraftEntity.java create mode 100644 src/api/java/thaumcraft/api/damagesource/DamageSourceThaumcraft.java create mode 100644 src/api/java/thaumcraft/api/entities/ITaintedMob.java create mode 100644 src/api/java/thaumcraft/api/nodes/INode.java create mode 100644 src/api/java/thaumcraft/api/nodes/IRevealer.java create mode 100644 src/api/java/thaumcraft/api/nodes/NodeModifier.java create mode 100644 src/api/java/thaumcraft/api/nodes/NodeType.java create mode 100644 src/api/java/thaumcraft/api/package-info.java create mode 100644 src/api/java/thaumcraft/api/potions/PotionFluxTaint.java create mode 100644 src/api/java/thaumcraft/api/potions/PotionVisExhaust.java create mode 100644 src/api/java/thaumcraft/api/research/IScanEventHandler.java create mode 100644 src/api/java/thaumcraft/api/research/ResearchCategories.java create mode 100644 src/api/java/thaumcraft/api/research/ResearchCategoryList.java create mode 100644 src/api/java/thaumcraft/api/research/ResearchItem.java create mode 100644 src/api/java/thaumcraft/api/research/ResearchPage.java create mode 100644 src/api/java/thaumcraft/api/research/ScanResult.java create mode 100644 src/api/java/thaumcraft/api/visnet/TileVisNode.java create mode 100644 src/api/java/thaumcraft/api/visnet/VisNetHandler.java create mode 100644 src/api/java/thaumcraft/api/wands/IWandFocus.java create mode 100644 src/api/java/thaumcraft/api/wands/IWandRodOnUpdate.java create mode 100644 src/api/java/thaumcraft/api/wands/IWandTriggerManager.java create mode 100644 src/api/java/thaumcraft/api/wands/IWandable.java create mode 100644 src/api/java/thaumcraft/api/wands/ItemFocusBasic.java create mode 100644 src/api/java/thaumcraft/api/wands/StaffRod.java create mode 100644 src/api/java/thaumcraft/api/wands/WandCap.java create mode 100644 src/api/java/thaumcraft/api/wands/WandRod.java create mode 100644 src/api/java/thaumcraft/api/wands/WandTriggerRegistry.java create mode 100644 src/api/java/truetyper/FontHelper.java create mode 100644 src/api/java/truetyper/FontLoader.java create mode 100644 src/api/java/truetyper/Formatter.java create mode 100644 src/api/java/truetyper/LICENSE.md create mode 100644 src/api/java/truetyper/README.md create mode 100644 src/api/java/truetyper/TrueTypeFont.java diff --git a/src/api/java/thaumcraft/api/IGoggles.java b/src/api/java/thaumcraft/api/IGoggles.java new file mode 100644 index 0000000..2f53d81 --- /dev/null +++ b/src/api/java/thaumcraft/api/IGoggles.java @@ -0,0 +1,22 @@ +package thaumcraft.api; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; + +/** + * + * @author Azanor + * + * Equipped head slot items that extend this class will be able to perform most functions that + * goggles of revealing can apart from view nodes which is handled by IRevealer. + * + */ + +public interface IGoggles { + + /* + * If this method returns true things like block essentia contents will be shown. + */ + public boolean showIngamePopups(ItemStack itemstack, EntityLivingBase player); + +} diff --git a/src/api/java/thaumcraft/api/IRepairable.java b/src/api/java/thaumcraft/api/IRepairable.java new file mode 100644 index 0000000..48c6dff --- /dev/null +++ b/src/api/java/thaumcraft/api/IRepairable.java @@ -0,0 +1,13 @@ +package thaumcraft.api; + + + +/** + * @author Azanor + * Items, armor and tools with this interface can receive the Repair enchantment. + * Repairs 1 point of durability every 10 seconds (2 for repair II) + */ +public interface IRepairable { + + +} diff --git a/src/api/java/thaumcraft/api/IRepairableExtended.java b/src/api/java/thaumcraft/api/IRepairableExtended.java new file mode 100644 index 0000000..3382712 --- /dev/null +++ b/src/api/java/thaumcraft/api/IRepairableExtended.java @@ -0,0 +1,17 @@ +package thaumcraft.api; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; + + + +/** + * @author Azanor + * Items, armor and tools with this interface can receive the Repair enchantment. + * Repairs 1 point of durability every 10 seconds (2 for repair II) + */ +public interface IRepairableExtended extends IRepairable { + + public boolean doRepair(ItemStack stack, EntityPlayer player, int enchantlevel); + +} diff --git a/src/api/java/thaumcraft/api/IRunicArmor.java b/src/api/java/thaumcraft/api/IRunicArmor.java new file mode 100644 index 0000000..5dd3110 --- /dev/null +++ b/src/api/java/thaumcraft/api/IRunicArmor.java @@ -0,0 +1,22 @@ +package thaumcraft.api; + +import net.minecraft.item.ItemStack; + +/** + * + * @author Azanor + * + * Armor or bauble slot items that implement this interface can provide runic shielding. + * Recharging, hardening, etc. is handled internally by thaumcraft. + * + */ + +public interface IRunicArmor { + + /** + * returns how much charge this item can provide. This is the base shielding value - any hardening is stored and calculated internally. + */ + public int getRunicCharge(ItemStack itemstack); + + +} diff --git a/src/api/java/thaumcraft/api/IScribeTools.java b/src/api/java/thaumcraft/api/IScribeTools.java new file mode 100644 index 0000000..8800fa5 --- /dev/null +++ b/src/api/java/thaumcraft/api/IScribeTools.java @@ -0,0 +1,14 @@ +package thaumcraft.api; + + +/** + * + * @author Azanor + * + * Interface used to identify scribing tool items used in research table + * + */ + +public interface IScribeTools { + +} diff --git a/src/api/java/thaumcraft/api/IVisDiscountGear.java b/src/api/java/thaumcraft/api/IVisDiscountGear.java new file mode 100644 index 0000000..3793ea3 --- /dev/null +++ b/src/api/java/thaumcraft/api/IVisDiscountGear.java @@ -0,0 +1,20 @@ +package thaumcraft.api; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import thaumcraft.api.aspects.Aspect; + + + + +/** + * @author Azanor + * ItemArmor with this interface will grant a discount to the vis cost of actions the wearer performs with casting wands. + * The amount returned is the percentage by which the cost is discounted. There is a built-int max discount of 50%, but + * individual items really shouldn't have a discount more than 5% + */ +public interface IVisDiscountGear { + + int getVisDiscount(ItemStack stack, EntityPlayer player, Aspect aspect); + +} diff --git a/src/api/java/thaumcraft/api/ItemApi.java b/src/api/java/thaumcraft/api/ItemApi.java new file mode 100644 index 0000000..25dda28 --- /dev/null +++ b/src/api/java/thaumcraft/api/ItemApi.java @@ -0,0 +1,70 @@ +package thaumcraft.api; + +import net.minecraft.block.Block; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import cpw.mods.fml.common.FMLLog; + +/** + * @author Azanor + * + * This is used to gain access to the items in my mod. + * I only give some examples and it will probably still + * require a bit of work for you to get hold of everything you need. + * + */ +public class ItemApi { + + public static ItemStack getItem(String itemString, int meta) { + ItemStack item = null; + + try { + String itemClass = "thaumcraft.common.config.ConfigItems"; + Object obj = Class.forName(itemClass).getField(itemString).get(null); + if (obj instanceof Item) { + item = new ItemStack((Item) obj,1,meta); + } else if (obj instanceof ItemStack) { + item = (ItemStack) obj; + } + } catch (Exception ex) { + FMLLog.warning("[Thaumcraft] Could not retrieve item identified by: " + itemString); + } + + return item; + } + + public static ItemStack getBlock(String itemString, int meta) { + ItemStack item = null; + + try { + String itemClass = "thaumcraft.common.config.ConfigBlocks"; + Object obj = Class.forName(itemClass).getField(itemString).get(null); + if (obj instanceof Block) { + item = new ItemStack((Block) obj,1,meta); + } else if (obj instanceof ItemStack) { + item = (ItemStack) obj; + } + } catch (Exception ex) { + FMLLog.warning("[Thaumcraft] Could not retrieve block identified by: " + itemString); + } + + return item; + } + + /** + * + * Some examples + * + * Casting Wands: + * itemWandCasting + * + * Resources: + * itemEssence, itemWispEssence, itemResource, itemShard, itemNugget, + * itemNuggetChicken, itemNuggetBeef, itemNuggetPork, itemTripleMeatTreat + * + * Research: + * itemResearchNotes, itemInkwell, itemThaumonomicon + * + */ + +} diff --git a/src/api/java/thaumcraft/api/ItemRunic.java b/src/api/java/thaumcraft/api/ItemRunic.java new file mode 100644 index 0000000..80251f5 --- /dev/null +++ b/src/api/java/thaumcraft/api/ItemRunic.java @@ -0,0 +1,21 @@ +package thaumcraft.api; + +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; + +public class ItemRunic extends Item implements IRunicArmor { + + int charge; + + public ItemRunic (int charge) + { + super(); + this.charge = charge; + } + + @Override + public int getRunicCharge(ItemStack itemstack) { + return charge; + } + +} diff --git a/src/api/java/thaumcraft/api/ThaumcraftApi.java b/src/api/java/thaumcraft/api/ThaumcraftApi.java new file mode 100644 index 0000000..bbd7a43 --- /dev/null +++ b/src/api/java/thaumcraft/api/ThaumcraftApi.java @@ -0,0 +1,536 @@ +package thaumcraft.api; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.ConcurrentHashMap; + +import net.minecraft.block.Block; +import net.minecraft.enchantment.Enchantment; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.Item.ToolMaterial; +import net.minecraft.item.ItemArmor.ArmorMaterial; +import net.minecraft.item.ItemStack; +import net.minecraftforge.common.util.EnumHelper; +import net.minecraftforge.oredict.OreDictionary; +import thaumcraft.api.aspects.Aspect; +import thaumcraft.api.aspects.AspectList; +import thaumcraft.api.crafting.CrucibleRecipe; +import thaumcraft.api.crafting.InfusionEnchantmentRecipe; +import thaumcraft.api.crafting.InfusionRecipe; +import thaumcraft.api.crafting.ShapedArcaneRecipe; +import thaumcraft.api.crafting.ShapelessArcaneRecipe; +import thaumcraft.api.research.IScanEventHandler; +import thaumcraft.api.research.ResearchCategories; +import thaumcraft.api.research.ResearchCategoryList; +import thaumcraft.api.research.ResearchItem; +import thaumcraft.api.research.ResearchPage; + + +/** + * @author Azanor + * + * + * IMPORTANT: If you are adding your own aspects to items it is a good idea to do it AFTER Thaumcraft adds its aspects, otherwise odd things may happen. + * + */ +public class ThaumcraftApi { + + //Materials + public static ToolMaterial toolMatThaumium = EnumHelper.addToolMaterial("THAUMIUM", 3, 400, 7F, 2, 22); + public static ToolMaterial toolMatVoid = EnumHelper.addToolMaterial("VOID", 4, 150, 8F, 3, 10); + public static ToolMaterial toolMatElemental = EnumHelper.addToolMaterial("THAUMIUM_ELEMENTAL", 3, 1500, 10F, 3, 18); + public static ArmorMaterial armorMatThaumium = EnumHelper.addArmorMaterial("THAUMIUM", 25, new int[] { 2, 6, 5, 2 }, 25); + public static ArmorMaterial armorMatSpecial = EnumHelper.addArmorMaterial("SPECIAL", 25, new int[] { 1, 3, 2, 1 }, 25); + public static ArmorMaterial armorMatThaumiumFortress = EnumHelper.addArmorMaterial("FORTRESS", 40, new int[] { 3, 7, 6, 3 }, 25); + public static ArmorMaterial armorMatVoid = EnumHelper.addArmorMaterial("VOID", 10, new int[] { 3, 7, 6, 3 }, 10); + public static ArmorMaterial armorMatVoidFortress = EnumHelper.addArmorMaterial("VOIDFORTRESS", 18, new int[] { 4, 8, 7, 4 }, 10); + + //Enchantment references + public static int enchantFrugal; + public static int enchantPotency; + public static int enchantWandFortune; + public static int enchantHaste; + public static int enchantRepair; + + //Miscellaneous + /** + * Portable Hole Block-id Blacklist. + * Simply add the block-id's of blocks you don't want the portable hole to go through. + */ + public static ArrayList portableHoleBlackList = new ArrayList(); + + + //RESEARCH///////////////////////////////////////// + public static ArrayList scanEventhandlers = new ArrayList(); + public static ArrayList scanEntities = new ArrayList(); + public static class EntityTagsNBT { + public EntityTagsNBT(String name, Object value) { + this.name = name; + this.value = value; + } + public String name; + public Object value; + } + public static class EntityTags { + public EntityTags(String entityName, AspectList aspects, EntityTagsNBT... nbts) { + this.entityName = entityName; + this.nbts = nbts; + this.aspects = aspects; + } + public String entityName; + public EntityTagsNBT[] nbts; + public AspectList aspects; + } + + /** + * not really working atm, so ignore it for now + * @param scanEventHandler + */ + public static void registerScanEventhandler(IScanEventHandler scanEventHandler) { + scanEventhandlers.add(scanEventHandler); + } + + /** + * This is used to add aspects to entities which you can then scan using a thaumometer. + * Also used to calculate vis drops from mobs. + * @param entityName + * @param aspects + * @param nbt you can specify certain nbt keys and their values + * to differentiate between mobs.
For example the normal and wither skeleton: + *
ThaumcraftApi.registerEntityTag("Skeleton", (new AspectList()).add(Aspect.DEATH, 5)); + *
ThaumcraftApi.registerEntityTag("Skeleton", (new AspectList()).add(Aspect.DEATH, 8), new NBTTagByte("SkeletonType",(byte) 1)); + */ + public static void registerEntityTag(String entityName, AspectList aspects, EntityTagsNBT... nbt ) { + scanEntities.add(new EntityTags(entityName,aspects,nbt)); + } + + //RECIPES///////////////////////////////////////// + private static ArrayList craftingRecipes = new ArrayList(); + private static HashMap smeltingBonus = new HashMap(); + + /** + * This method is used to determine what bonus items are generated when the infernal furnace smelts items + * @param in The input of the smelting operation. e.g. new ItemStack(Block.oreGold) + * @param out The bonus item that can be produced from the smelting operation e.g. new ItemStack(nuggetGold,0,0). + * Stacksize should be 0 unless you want to guarantee that at least 1 item is always produced. + */ + public static void addSmeltingBonus(ItemStack in, ItemStack out) { + smeltingBonus.put( + Arrays.asList(in.getItem(),in.getItemDamage()), + new ItemStack(out.getItem(),0,out.getItemDamage())); + } + + /** + * This method is used to determine what bonus items are generated when the infernal furnace smelts items + * @param in The ore dictionary input of the smelting operation. e.g. "oreGold" + * @param out The bonus item that can be produced from the smelting operation e.g. new ItemStack(nuggetGold,0,0). + * Stacksize should be 0 unless you want to guarantee that at least 1 item is always produced. + */ + public static void addSmeltingBonus(String in, ItemStack out) { + smeltingBonus.put( in, new ItemStack(out.getItem(),0,out.getItemDamage())); + } + + /** + * Returns the bonus item produced from a smelting operation in the infernal furnace + * @param in The input of the smelting operation. e.g. new ItemStack(oreGold) + * @return the The bonus item that can be produced + */ + public static ItemStack getSmeltingBonus(ItemStack in) { + ItemStack out = smeltingBonus.get(Arrays.asList(in.getItem(),in.getItemDamage())); + if (out==null) { + out = smeltingBonus.get(Arrays.asList(in.getItem(),OreDictionary.WILDCARD_VALUE)); + } + if (out==null) { + String od = OreDictionary.getOreName( OreDictionary.getOreID(in)); + out = smeltingBonus.get(od); + } + return out; + } + + public static List getCraftingRecipes() { + return craftingRecipes; + } + + /** + * @param research the research key required for this recipe to work. Leave blank if it will work without research + * @param result the recipe output + * @param aspects the vis cost per aspect. + * @param recipe The recipe. Format is exactly the same as vanilla recipes. Input itemstacks are NBT sensitive. + */ + public static ShapedArcaneRecipe addArcaneCraftingRecipe(String research, ItemStack result, AspectList aspects, Object ... recipe) + { + ShapedArcaneRecipe r= new ShapedArcaneRecipe(research, result, aspects, recipe); + craftingRecipes.add(r); + return r; + } + + /** + * @param research the research key required for this recipe to work. Leave blank if it will work without research + * @param result the recipe output + * @param aspects the vis cost per aspect + * @param recipe The recipe. Format is exactly the same as vanilla shapeless recipes. Input itemstacks are NBT sensitive. + */ + public static ShapelessArcaneRecipe addShapelessArcaneCraftingRecipe(String research, ItemStack result, AspectList aspects, Object ... recipe) + { + ShapelessArcaneRecipe r = new ShapelessArcaneRecipe(research, result, aspects, recipe); + craftingRecipes.add(r); + return r; + } + + /** + * @param research the research key required for this recipe to work. Leave blank if it will work without research + * @param result the recipe output. It can either be an itemstack or an nbt compound tag that will be added to the central item + * @param instability a number that represents the N in 1000 chance for the infusion altar to spawn an + * instability effect each second while the crafting is in progress + * @param aspects the essentia cost per aspect. + * @param aspects input the central item to be infused + * @param recipe An array of items required to craft this. Input itemstacks are NBT sensitive. + * Infusion crafting components are automatically "fuzzy" and the oredict will be checked for possible matches. + * + */ + public static InfusionRecipe addInfusionCraftingRecipe(String research, Object result, int instability, AspectList aspects, ItemStack input,ItemStack[] recipe) + { + if (!(result instanceof ItemStack || result instanceof Object[])) return null; + InfusionRecipe r= new InfusionRecipe(research, result, instability, aspects, input, recipe); + craftingRecipes.add(r); + return r; + } + + /** + * @param research the research key required for this recipe to work. Leave blank if it will work without research + * @param enchantment the enchantment that will be applied to the item + * @param instability a number that represents the N in 1000 chance for the infusion altar to spawn an + * instability effect each second while the crafting is in progress + * @param aspects the essentia cost per aspect. + * @param recipe An array of items required to craft this. Input itemstacks are NBT sensitive. + * Infusion crafting components are automatically "fuzzy" and the oredict will be checked for possible matches. + * + */ + public static InfusionEnchantmentRecipe addInfusionEnchantmentRecipe(String research, Enchantment enchantment, int instability, AspectList aspects, ItemStack[] recipe) + { + InfusionEnchantmentRecipe r= new InfusionEnchantmentRecipe(research, enchantment, instability, aspects, recipe); + craftingRecipes.add(r); + return r; + } + + /** + * @param stack the recipe result + * @return the recipe + */ + public static InfusionRecipe getInfusionRecipe(ItemStack res) { + for (Object r:getCraftingRecipes()) { + if (r instanceof InfusionRecipe) { + if (((InfusionRecipe)r).getRecipeOutput() instanceof ItemStack) { + if (((ItemStack) ((InfusionRecipe)r).getRecipeOutput()).isItemEqual(res)) + return (InfusionRecipe)r; + } + } + } + return null; + } + + + /** + * @param key the research key required for this recipe to work. + * @param result the output result + * @param catalyst an itemstack of the catalyst or a string if it is an ore dictionary item + * @param cost the vis cost + * @param tags the aspects required to craft this + */ + public static CrucibleRecipe addCrucibleRecipe(String key, ItemStack result, Object catalyst, AspectList tags) { + CrucibleRecipe rc = new CrucibleRecipe(key, result, catalyst, tags); + getCraftingRecipes().add(rc); + return rc; + } + + + /** + * @param stack the recipe result + * @return the recipe + */ + public static CrucibleRecipe getCrucibleRecipe(ItemStack stack) { + for (Object r:getCraftingRecipes()) { + if (r instanceof CrucibleRecipe) { + if (((CrucibleRecipe)r).getRecipeOutput().isItemEqual(stack)) + return (CrucibleRecipe)r; + } + } + return null; + } + + /** + * @param hash the unique recipe code + * @return the recipe + */ + public static CrucibleRecipe getCrucibleRecipeFromHash(int hash) { + for (Object r:getCraftingRecipes()) { + if (r instanceof CrucibleRecipe) { + if (((CrucibleRecipe)r).hash==hash) + return (CrucibleRecipe)r; + } + } + return null; + } + + /** + * Used by the thaumonomicon drilldown feature. + * @param stack the item + * @return the thaumcraft recipe key that produces that item. + */ + private static HashMap keyCache = new HashMap(); + + public static Object[] getCraftingRecipeKey(EntityPlayer player, ItemStack stack) { + int[] key = new int[] {Item.getIdFromItem(stack.getItem()),stack.getItemDamage()}; + if (keyCache.containsKey(key)) { + if (keyCache.get(key)==null) return null; + if (ThaumcraftApiHelper.isResearchComplete(player.getCommandSenderName(), (String)(keyCache.get(key))[0])) + return keyCache.get(key); + else + return null; + } + for (ResearchCategoryList rcl:ResearchCategories.researchCategories.values()) { + for (ResearchItem ri:rcl.research.values()) { + if (ri.getPages()==null) continue; + for (int a=0;a objectTags = new ConcurrentHashMap(); + + /** + * Checks to see if the passed item/block already has aspects associated with it. + * @param id + * @param meta + * @return + */ + public static boolean exists(Item item, int meta) { + AspectList tmp = ThaumcraftApi.objectTags.get(Arrays.asList(item,meta)); + if (tmp==null) { + tmp = ThaumcraftApi.objectTags.get(Arrays.asList(item,OreDictionary.WILDCARD_VALUE)); + if (meta==OreDictionary.WILDCARD_VALUE && tmp==null) { + int index=0; + do { + tmp = ThaumcraftApi.objectTags.get(Arrays.asList(item,index)); + index++; + } while (index<16 && tmp==null); + } + if (tmp==null) return false; + } + + return true; + } + + /** + * Used to assign apsects to the given item/block. Here is an example of the declaration for cobblestone:

+ * ThaumcraftApi.registerObjectTag(new ItemStack(Blocks.cobblestone), (new AspectList()).add(Aspect.ENTROPY, 1).add(Aspect.EARTH, 1)); + * @param item the item passed. Pass OreDictionary.WILDCARD_VALUE if all damage values of this item/block should have the same aspects + * @param aspects A ObjectTags object of the associated aspects + */ + public static void registerObjectTag(ItemStack item, AspectList aspects) { + if (aspects==null) aspects=new AspectList(); + try { + objectTags.put(Arrays.asList(item.getItem(),item.getItemDamage()), aspects); + } catch (Exception e) {} + } + + + /** + * Used to assign apsects to the given item/block. Here is an example of the declaration for cobblestone:

+ * ThaumcraftApi.registerObjectTag(new ItemStack(Blocks.cobblestone), new int[]{0,1}, (new AspectList()).add(Aspect.ENTROPY, 1).add(Aspect.EARTH, 1)); + * @param item + * @param meta A range of meta values if you wish to lump several item meta's together as being the "same" item (i.e. stair orientations) + * @param aspects A ObjectTags object of the associated aspects + */ + public static void registerObjectTag(ItemStack item, int[] meta, AspectList aspects) { + if (aspects==null) aspects=new AspectList(); + try { + objectTags.put(Arrays.asList(item.getItem(),meta), aspects); + } catch (Exception e) {} + } + + /** + * Used to assign apsects to the given ore dictionary item. + * @param oreDict the ore dictionary name + * @param aspects A ObjectTags object of the associated aspects + */ + public static void registerObjectTag(String oreDict, AspectList aspects) { + if (aspects==null) aspects=new AspectList(); + ArrayList ores = OreDictionary.getOres(oreDict); + if (ores!=null && ores.size()>0) { + for (ItemStack ore:ores) { + try { + objectTags.put(Arrays.asList(ore.getItem(), ore.getItemDamage()), aspects); + } catch (Exception e) {} + } + } + } + + /** + * Used to assign aspects to the given item/block. + * Attempts to automatically generate aspect tags by checking registered recipes. + * Here is an example of the declaration for pistons:

+ * ThaumcraftApi.registerComplexObjectTag(new ItemStack(Blocks.cobblestone), (new AspectList()).add(Aspect.MECHANISM, 2).add(Aspect.MOTION, 4)); + * IMPORTANT - this should only be used if you are not happy with the default aspects the object would be assigned. + * @param item, pass OreDictionary.WILDCARD_VALUE to meta if all damage values of this item/block should have the same aspects + * @param aspects A ObjectTags object of the associated aspects + */ + public static void registerComplexObjectTag(ItemStack item, AspectList aspects ) { + if (!exists(item.getItem(),item.getItemDamage())) { + AspectList tmp = ThaumcraftApiHelper.generateTags(item.getItem(), item.getItemDamage()); + if (tmp != null && tmp.size()>0) { + for(Aspect tag:tmp.getAspects()) { + aspects.add(tag, tmp.getAmount(tag)); + } + } + registerObjectTag(item,aspects); + } else { + AspectList tmp = ThaumcraftApiHelper.getObjectAspects(item); + for(Aspect tag:aspects.getAspects()) { + tmp.merge(tag, tmp.getAmount(tag)); + } + registerObjectTag(item,tmp); + } + } + + //WARP /////////////////////////////////////////////////////////////////////////////////////// + private static HashMap warpMap = new HashMap(); + + /** + * This method is used to determine how much warp is gained if the item is crafted + * @param craftresult The item crafted + * @param amount how much warp is gained + */ + public static void addWarpToItem(ItemStack craftresult, int amount) { + warpMap.put(Arrays.asList(craftresult.getItem(),craftresult.getItemDamage()),amount); + } + + /** + * This method is used to determine how much warp is gained if the sent item is crafted + * @param in The item crafted + * @param amount how much warp is gained + */ + public static void addWarpToResearch(String research, int amount) { + warpMap.put(research, amount); + } + + /** + * Returns how much warp is gained from the item or research passed in + * @param in itemstack or string + * @return how much warp it will give + */ + public static int getWarp(Object in) { + if (in==null) return 0; + if (in instanceof ItemStack && warpMap.containsKey(Arrays.asList(((ItemStack)in).getItem(),((ItemStack)in).getItemDamage()))) { + return warpMap.get(Arrays.asList(((ItemStack)in).getItem(),((ItemStack)in).getItemDamage())); + } else + if (in instanceof String && warpMap.containsKey((String)in)) { + return warpMap.get((String)in); + } + return 0; + } + + //CROPS ////////////////////////////////////////////////////////////////////////////////////////// + + /** + * To define mod crops you need to use FMLInterModComms in your @Mod.Init method. + * There are two 'types' of crops you can add. Standard crops and clickable crops. + * + * Standard crops work like normal vanilla crops - they grow until a certain metadata + * value is reached and you harvest them by destroying the block and collecting the blocks. + * You need to create and ItemStack that tells the golem what block id and metadata represents + * the crop when fully grown. Sending a metadata of [OreDictionary.WILDCARD_VALUE] will mean the metadata won't get + * checked. + * Example for vanilla wheat: + * FMLInterModComms.sendMessage("Thaumcraft", "harvestStandardCrop", new ItemStack(Block.crops,1,7)); + * + * Clickable crops are crops that you right click to gather their bounty instead of destroying them. + * As for standard crops, you need to create and ItemStack that tells the golem what block id + * and metadata represents the crop when fully grown. The golem will trigger the blocks onBlockActivated method. + * Sending a metadata of [OreDictionary.WILDCARD_VALUE] will mean the metadata won't get checked. + * Example (this will technically do nothing since clicking wheat does nothing, but you get the idea): + * FMLInterModComms.sendMessage("Thaumcraft", "harvestClickableCrop", new ItemStack(Block.crops,1,7)); + * + * Stacked crops (like reeds) are crops that you wish the bottom block should remain after harvesting. + * As for standard crops, you need to create and ItemStack that tells the golem what block id + * and metadata represents the crop when fully grown. Sending a metadata of [OreDictionary.WILDCARD_VALUE] will mean the actualy md won't get + * checked. If it has the order upgrade it will only harvest if the crop is more than one block high. + * Example: + * FMLInterModComms.sendMessage("Thaumcraft", "harvestStackedCrop", new ItemStack(Block.reed,1,7)); + */ + + //NATIVE CLUSTERS ////////////////////////////////////////////////////////////////////////////////// + + /** + * You can define certain ores that will have a chance to produce native clusters via FMLInterModComms + * in your @Mod.Init method using the "nativeCluster" string message. + * The format should be: + * "[ore item/block id],[ore item/block metadata],[cluster item/block id],[cluster item/block metadata],[chance modifier float]" + * + * NOTE: The chance modifier is a multiplier applied to the default chance for that cluster to be produced (default 27.5% for a pickaxe of the core) + * + * Example for vanilla iron ore to produce one of my own native iron clusters (assuming default id's) at double the default chance: + * FMLInterModComms.sendMessage("Thaumcraft", "nativeCluster","15,0,25016,16,2.0"); + */ + + //LAMP OF GROWTH BLACKLIST /////////////////////////////////////////////////////////////////////////// + /** + * You can blacklist crops that should not be effected by the Lamp of Growth via FMLInterModComms + * in your @Mod.Init method using the "lampBlacklist" itemstack message. + * Sending a metadata of [OreDictionary.WILDCARD_VALUE] will mean the metadata won't get checked. + * Example for vanilla wheat: + * FMLInterModComms.sendMessage("Thaumcraft", "lampBlacklist", new ItemStack(Block.crops,1,OreDictionary.WILDCARD_VALUE)); + */ + + //DIMENSION BLACKLIST /////////////////////////////////////////////////////////////////////////// + /** + * You can blacklist a dimension to not spawn certain thaumcraft features + * in your @Mod.Init method using the "dimensionBlacklist" string message in the format "[dimension]:[level]" + * The level values are as follows: + * [0] stop all tc spawning and generation + * [1] allow ore and node generation + * [2] allow mob spawning + * [3] allow ore and node gen + mob spawning + * Example: + * FMLInterModComms.sendMessage("Thaumcraft", "dimensionBlacklist", "15:1"); + */ + + //BIOME BLACKLIST /////////////////////////////////////////////////////////////////////////// + /** + * You can blacklist a biome to not spawn certain thaumcraft features + * in your @Mod.Init method using the "biomeBlacklist" string message in the format "[biome id]:[level]" + * The level values are as follows: + * [0] stop all tc spawning and generation + * [1] allow ore and node generation + * [2] allow mob spawning + * [3] allow ore and node gen + mob spawning + * Example: + * FMLInterModComms.sendMessage("Thaumcraft", "biomeBlacklist", "180:2"); + */ +} diff --git a/src/api/java/thaumcraft/api/ThaumcraftApiHelper.java b/src/api/java/thaumcraft/api/ThaumcraftApiHelper.java new file mode 100644 index 0000000..7f95dc4 --- /dev/null +++ b/src/api/java/thaumcraft/api/ThaumcraftApiHelper.java @@ -0,0 +1,293 @@ +package thaumcraft.api; + +import java.lang.reflect.Method; +import java.util.HashMap; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.world.IBlockAccess; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.oredict.OreDictionary; +import thaumcraft.api.aspects.Aspect; +import thaumcraft.api.aspects.AspectList; +import thaumcraft.api.aspects.IEssentiaTransport; +import cpw.mods.fml.common.FMLLog; + +public class ThaumcraftApiHelper { + + public static AspectList cullTags(AspectList temp) { + AspectList temp2 = new AspectList(); + for (Aspect tag:temp.getAspects()) { + if (tag!=null) + temp2.add(tag, temp.getAmount(tag)); + } + while (temp2!=null && temp2.size()>10) { + Aspect lowest = null; + int low = Integer.MAX_VALUE; + for (Aspect tag:temp2.getAspects()) { + if (tag==null) continue; + if (temp2.getAmount(tag) allAspects= new HashMap(); + private static HashMap allCompoundAspects= new HashMap(); + + public static AspectList getAllAspects(int amount) { + if (allAspects.get(amount)==null) { + AspectList al = new AspectList(); + for (Aspect aspect:Aspect.aspects.values()) { + al.add(aspect, amount); + } + allAspects.put(amount, al); + } + return allAspects.get(amount); + } + + public static AspectList getAllCompoundAspects(int amount) { + if (allCompoundAspects.get(amount)==null) { + AspectList al = new AspectList(); + for (Aspect aspect:Aspect.getCompoundAspects()) { + al.add(aspect, amount); + } + allCompoundAspects.put(amount, al); + } + return allCompoundAspects.get(amount); + } + + static Method consumeVisFromWand; + /** + * Use to subtract vis from a wand for most operations + * Wands store vis differently so "real" vis costs need to be multiplied by 100 before calling this method + * @param wand the wand itemstack + * @param player the player using the wand + * @param cost the cost of the operation. + * @param doit actually subtract the vis from the wand if true - if false just simulate the result + * @param crafting is this a crafting operation or not - if + * false then things like frugal and potency will apply to the costs + * @return was the vis successfully subtracted + */ + public static boolean consumeVisFromWand(ItemStack wand, EntityPlayer player, + AspectList cost, boolean doit, boolean crafting) { + boolean ot = false; + try { + if(consumeVisFromWand == null) { + Class fake = Class.forName("thaumcraft.common.items.wands.ItemWandCasting"); + consumeVisFromWand = fake.getMethod("consumeAllVis", + ItemStack.class, EntityPlayer.class, AspectList.class, boolean.class, boolean.class); + } + ot = (Boolean) consumeVisFromWand.invoke( + consumeVisFromWand.getDeclaringClass().cast(wand.getItem()), wand, player, cost, doit, crafting); + } catch(Exception ex) { + FMLLog.warning("[Thaumcraft API] Could not invoke thaumcraft.common.items.wands.ItemWandCasting method consumeAllVis"); + } + return ot; + } + + static Method consumeVisFromWandCrafting; + /** + * Subtract vis for use by a crafting mechanic. Costs are calculated slightly + * differently and things like the frugal enchant is ignored + * Must NOT be multiplied by 100 - send the actual vis cost + * @param wand the wand itemstack + * @param player the player using the wand + * @param cost the cost of the operation. + * @param doit actually subtract the vis from the wand if true - if false just simulate the result + * @return was the vis successfully subtracted + */ + public static boolean consumeVisFromWandCrafting(ItemStack wand, EntityPlayer player, + AspectList cost, boolean doit) { + boolean ot = false; + try { + if(consumeVisFromWandCrafting == null) { + Class fake = Class.forName("thaumcraft.common.items.wands.ItemWandCasting"); + consumeVisFromWandCrafting = fake.getMethod("consumeAllVisCrafting", + ItemStack.class, EntityPlayer.class, AspectList.class, boolean.class); + } + ot = (Boolean) consumeVisFromWandCrafting.invoke( + consumeVisFromWandCrafting.getDeclaringClass().cast(wand.getItem()), wand, player, cost, doit); + } catch(Exception ex) { + FMLLog.warning("[Thaumcraft API] Could not invoke thaumcraft.common.items.wands.ItemWandCasting method consumeAllVisCrafting"); + } + return ot; + } + + static Method consumeVisFromInventory; + /** + * Subtract vis from a wand the player is carrying. Works like consumeVisFromWand in that actual vis + * costs should be multiplied by 100. The costs are handled like crafting however and things like + * frugal don't effect them + * @param player the player using the wand + * @param cost the cost of the operation. + * @return was the vis successfully subtracted + */ + public static boolean consumeVisFromInventory(EntityPlayer player, AspectList cost) { + boolean ot = false; + try { + if(consumeVisFromInventory == null) { + Class fake = Class.forName("thaumcraft.common.items.wands.WandManager"); + consumeVisFromInventory = fake.getMethod("consumeVisFromInventory", + EntityPlayer.class, AspectList.class); + } + ot = (Boolean) consumeVisFromInventory.invoke(null, player, cost); + } catch(Exception ex) { + FMLLog.warning("[Thaumcraft API] Could not invoke thaumcraft.common.items.wands.WandManager method consumeVisFromInventory"); + } + return ot; + } + + + static Method addWarpToPlayer; + /** + * This adds permanents or temporary warp to a player. It will automatically be synced clientside + * @param player the player using the wand + * @param amount how much warp to add + * @param amount how much warp to add. Negative amounts are only valid for temporary warp + * @param temporary add temporary warp instead of permanent + * @param amount how much warp to add + */ + public static void addWarpToPlayer(EntityPlayer player, int amount, boolean temporary) { + boolean ot = false; + try { + if(addWarpToPlayer == null) { + Class fake = Class.forName("thaumcraft.common.Thaumcraft"); + addWarpToPlayer = fake.getMethod("addWarpToPlayer", + EntityPlayer.class, int.class, boolean.class); + } + addWarpToPlayer.invoke(null, player, amount, temporary); + } catch(Exception ex) { + FMLLog.warning("[Thaumcraft API] Could not invoke thaumcraft.common.Thaumcraft method addWarpToPlayer"); + } + } +} diff --git a/src/api/java/thaumcraft/api/TileThaumcraft.java b/src/api/java/thaumcraft/api/TileThaumcraft.java new file mode 100644 index 0000000..56ccae8 --- /dev/null +++ b/src/api/java/thaumcraft/api/TileThaumcraft.java @@ -0,0 +1,63 @@ +package thaumcraft.api; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.network.NetworkManager; +import net.minecraft.network.Packet; +import net.minecraft.network.play.server.S35PacketUpdateTileEntity; +import net.minecraft.tileentity.TileEntity; + +/** + * + * @author azanor + * + * Custom tile entity class I use for most of my tile entities. Setup in such a way that only + * the nbt data within readCustomNBT / writeCustomNBT will be sent to the client when the tile + * updates. Apart from all the normal TE data that gets sent that is. + * + */ +public class TileThaumcraft extends TileEntity { + + //NBT stuff + + @Override + public void readFromNBT(NBTTagCompound nbttagcompound) + { + super.readFromNBT(nbttagcompound); + readCustomNBT(nbttagcompound); + } + + public void readCustomNBT(NBTTagCompound nbttagcompound) + { + //TODO + } + + @Override + public void writeToNBT(NBTTagCompound nbttagcompound) + { + super.writeToNBT(nbttagcompound); + writeCustomNBT(nbttagcompound); + } + + public void writeCustomNBT(NBTTagCompound nbttagcompound) + { + //TODO + } + + //Client Packet stuff + @Override + public Packet getDescriptionPacket() { + NBTTagCompound nbttagcompound = new NBTTagCompound(); + this.writeCustomNBT(nbttagcompound); + return new S35PacketUpdateTileEntity(this.xCoord, this.yCoord, this.zCoord, -999, nbttagcompound); + } + + @Override + public void onDataPacket(NetworkManager net, S35PacketUpdateTileEntity pkt) { + super.onDataPacket(net, pkt); + this.readCustomNBT(pkt.func_148857_g()); + } + + + + +} diff --git a/src/api/java/thaumcraft/api/WorldCoordinates.java b/src/api/java/thaumcraft/api/WorldCoordinates.java new file mode 100644 index 0000000..6c620af --- /dev/null +++ b/src/api/java/thaumcraft/api/WorldCoordinates.java @@ -0,0 +1,117 @@ +package thaumcraft.api; + +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; + +public class WorldCoordinates implements Comparable +{ + public int x; + + /** the y coordinate */ + public int y; + + /** the z coordinate */ + public int z; + + public int dim; + + public WorldCoordinates() {} + + public WorldCoordinates(int par1, int par2, int par3, int d) + { + this.x = par1; + this.y = par2; + this.z = par3; + this.dim = d; + } + + public WorldCoordinates(TileEntity tile) + { + this.x = tile.xCoord; + this.y = tile.yCoord; + this.z = tile.zCoord; + this.dim = tile.getWorldObj().provider.dimensionId; + } + + public WorldCoordinates(WorldCoordinates par1ChunkCoordinates) + { + this.x = par1ChunkCoordinates.x; + this.y = par1ChunkCoordinates.y; + this.z = par1ChunkCoordinates.z; + this.dim = par1ChunkCoordinates.dim; + } + + public boolean equals(Object par1Obj) + { + if (!(par1Obj instanceof WorldCoordinates)) + { + return false; + } + else + { + WorldCoordinates coordinates = (WorldCoordinates)par1Obj; + return this.x == coordinates.x && this.y == coordinates.y && this.z == coordinates.z && this.dim == coordinates.dim ; + } + } + + public int hashCode() + { + return this.x + this.y << 8 + this.z << 16 + this.dim << 24; + } + + /** + * Compare the coordinate with another coordinate + */ + public int compareWorldCoordinate(WorldCoordinates par1) + { + return this.dim == par1.dim ? ( + this.y == par1.y ? (this.z == par1.z ? this.x - par1.x : this.z - par1.z) : this.y - par1.y) : -1; + } + + public void set(int par1, int par2, int par3, int d) + { + this.x = par1; + this.y = par2; + this.z = par3; + this.dim = d; + } + + /** + * Returns the squared distance between this coordinates and the coordinates given as argument. + */ + public float getDistanceSquared(int par1, int par2, int par3) + { + float f = (float)(this.x - par1); + float f1 = (float)(this.y - par2); + float f2 = (float)(this.z - par3); + return f * f + f1 * f1 + f2 * f2; + } + + /** + * Return the squared distance between this coordinates and the ChunkCoordinates given as argument. + */ + public float getDistanceSquaredToWorldCoordinates(WorldCoordinates par1ChunkCoordinates) + { + return this.getDistanceSquared(par1ChunkCoordinates.x, par1ChunkCoordinates.y, par1ChunkCoordinates.z); + } + + public int compareTo(Object par1Obj) + { + return this.compareWorldCoordinate((WorldCoordinates)par1Obj); + } + + public void readNBT(NBTTagCompound nbt) { + this.x = nbt.getInteger("w_x"); + this.y = nbt.getInteger("w_y"); + this.z = nbt.getInteger("w_z"); + this.dim = nbt.getInteger("w_d"); + } + + public void writeNBT(NBTTagCompound nbt) { + nbt.setInteger("w_x",x); + nbt.setInteger("w_y",y); + nbt.setInteger("w_z",z); + nbt.setInteger("w_d",dim); + } + +} diff --git a/src/api/java/thaumcraft/api/aspects/Aspect.java b/src/api/java/thaumcraft/api/aspects/Aspect.java new file mode 100644 index 0000000..3c33a4a --- /dev/null +++ b/src/api/java/thaumcraft/api/aspects/Aspect.java @@ -0,0 +1,201 @@ +package thaumcraft.api.aspects; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; + +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; + +import org.apache.commons.lang3.text.WordUtils; + +public class Aspect { + + String tag; + Aspect[] components; + int color; + private String chatcolor; + ResourceLocation image; + int blend; + + /** + * Use this constructor to register your own aspects. + * @param tag the key that will be used to reference this aspect, as well as its latin display name + * @param color color to display the tag in + * @param components the aspects this one is formed from + * @param image ResourceLocation pointing to a 32x32 icon of the aspect + * @param blend GL11 blendmode (1 or 771). Used for rendering nodes. Default is 1 + */ + public Aspect(String tag, int color, Aspect[] components, ResourceLocation image, int blend) { + if (aspects.containsKey(tag)) throw new IllegalArgumentException(tag+" already registered!"); + this.tag = tag; + this.components = components; + this.color = color; + this.image = image; + this.blend = blend; + aspects.put(tag, this); + } + + /** + * Shortcut constructor I use for the default aspects - you shouldn't be using this. + */ + public Aspect(String tag, int color, Aspect[] components) { + this(tag,color,components,new ResourceLocation("thaumcraft","textures/aspects/"+tag.toLowerCase()+".png"),1); + } + + /** + * Shortcut constructor I use for the default aspects - you shouldn't be using this. + */ + public Aspect(String tag, int color, Aspect[] components, int blend) { + this(tag,color,components,new ResourceLocation("thaumcraft","textures/aspects/"+tag.toLowerCase()+".png"),blend); + } + + /** + * Shortcut constructor I use for the primal aspects - + * you shouldn't use this as making your own primal aspects will break all the things. + */ + public Aspect(String tag, int color, String chatcolor, int blend) { + this(tag,color,(Aspect[])null, blend); + this.setChatcolor(chatcolor); + } + + public int getColor() { + return color; + } + + public String getName() { + return WordUtils.capitalizeFully(tag); + } + + public String getLocalizedDescription() { + return StatCollector.translateToLocal("tc.aspect."+tag); + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public Aspect[] getComponents() { + return components; + } + + public void setComponents(Aspect[] components) { + this.components = components; + } + + public ResourceLocation getImage() { + return image; + } + + public static Aspect getAspect(String tag) { + return aspects.get(tag); + } + + public int getBlend() { + return blend; + } + + public void setBlend(int blend) { + this.blend = blend; + } + + public boolean isPrimal() { + return getComponents()==null || getComponents().length!=2; + } + + /////////////////////////////// + public static ArrayList getPrimalAspects() { + ArrayList primals = new ArrayList(); + Collection pa = aspects.values(); + for (Aspect aspect:pa) { + if (aspect.isPrimal()) primals.add(aspect); + } + return primals; + } + + public static ArrayList getCompoundAspects() { + ArrayList compounds = new ArrayList(); + Collection pa = aspects.values(); + for (Aspect aspect:pa) { + if (!aspect.isPrimal()) compounds.add(aspect); + } + return compounds; + } + + public String getChatcolor() { + return chatcolor; + } + + public void setChatcolor(String chatcolor) { + this.chatcolor = chatcolor; + } + + + /////////////////////////////// + public static LinkedHashMap aspects = new LinkedHashMap(); + + //PRIMAL + public static final Aspect AIR = new Aspect("aer",0xffff7e,"e",1); + public static final Aspect EARTH = new Aspect("terra",0x56c000,"2",1); + public static final Aspect FIRE = new Aspect("ignis",0xff5a01,"c",1); + public static final Aspect WATER = new Aspect("aqua",0x3cd4fc,"3",1); + public static final Aspect ORDER = new Aspect("ordo",0xd5d4ec,"7",1); + public static final Aspect ENTROPY = new Aspect("perditio",0x404040,"8",771); + + //SECONDARY + public static final Aspect VOID = new Aspect("vacuos",0x888888, new Aspect[] {AIR, ENTROPY},771); + public static final Aspect LIGHT = new Aspect("lux",0xfff663, new Aspect[] {AIR, FIRE}); + public static final Aspect WEATHER = new Aspect("tempestas",0xFFFFFF, new Aspect[] {AIR, WATER}); + public static final Aspect MOTION = new Aspect("motus",0xcdccf4, new Aspect[] {AIR, ORDER}); + public static final Aspect COLD = new Aspect("gelum",0xe1ffff, new Aspect[] {FIRE, ENTROPY}); + public static final Aspect CRYSTAL = new Aspect("vitreus",0x80ffff, new Aspect[] {EARTH, ORDER}); + public static final Aspect LIFE = new Aspect("victus",0xde0005, new Aspect[] {WATER, EARTH}); + public static final Aspect POISON = new Aspect("venenum",0x89f000, new Aspect[] {WATER, ENTROPY}); + public static final Aspect ENERGY = new Aspect("potentia",0xc0ffff, new Aspect[] {ORDER, FIRE}); + public static final Aspect EXCHANGE = new Aspect("permutatio",0x578357, new Aspect[] {ENTROPY, ORDER}); +// public static final Aspect ?? = new Aspect("??",0xcdccf4, new Aspect[] {AIR, EARTH}); +// public static final Aspect ?? = new Aspect("??",0xcdccf4, new Aspect[] {FIRE, EARTH}); +// public static final Aspect ?? = new Aspect("??",0xcdccf4, new Aspect[] {FIRE, WATER}); +// public static final Aspect ?? = new Aspect("??",0xcdccf4, new Aspect[] {ORDER, WATER}); +// public static final Aspect ?? = new Aspect("??",0xcdccf4, new Aspect[] {EARTH, ENTROPY}); + + //TERTIARY + public static final Aspect METAL = new Aspect("metallum",0xb5b5cd, new Aspect[] {EARTH, CRYSTAL}); + public static final Aspect DEATH = new Aspect("mortuus",0x887788, new Aspect[] {LIFE, ENTROPY}); + public static final Aspect FLIGHT = new Aspect("volatus",0xe7e7d7, new Aspect[] {AIR, MOTION}); + public static final Aspect DARKNESS = new Aspect("tenebrae",0x222222, new Aspect[] {VOID, LIGHT}); + public static final Aspect SOUL = new Aspect("spiritus",0xebebfb, new Aspect[] {LIFE, DEATH}); + public static final Aspect HEAL = new Aspect("sano",0xff2f34, new Aspect[] {LIFE, ORDER}); + public static final Aspect TRAVEL = new Aspect("iter",0xe0585b, new Aspect[] {MOTION, EARTH}); + public static final Aspect ELDRITCH = new Aspect("alienis",0x805080, new Aspect[] {VOID, DARKNESS}); + public static final Aspect MAGIC = new Aspect("praecantatio",0x9700c0, new Aspect[] {VOID, ENERGY}); + public static final Aspect AURA = new Aspect("auram",0xffc0ff, new Aspect[] {MAGIC, AIR}); + public static final Aspect TAINT = new Aspect("vitium",0x800080, new Aspect[] {MAGIC, ENTROPY}); + public static final Aspect SLIME = new Aspect("limus",0x01f800, new Aspect[] {LIFE, WATER}); + public static final Aspect PLANT = new Aspect("herba",0x01ac00, new Aspect[] {LIFE, EARTH}); + public static final Aspect TREE = new Aspect("arbor",0x876531, new Aspect[] {AIR, PLANT}); + public static final Aspect BEAST = new Aspect("bestia",0x9f6409, new Aspect[] {MOTION, LIFE}); + public static final Aspect FLESH = new Aspect("corpus",0xee478d, new Aspect[] {DEATH, BEAST}); + public static final Aspect UNDEAD = new Aspect("exanimis",0x3a4000, new Aspect[] {MOTION, DEATH}); + public static final Aspect MIND = new Aspect("cognitio",0xffc2b3, new Aspect[] {EARTH, SOUL}); + public static final Aspect SENSES = new Aspect("sensus",0x0fd9ff, new Aspect[] {AIR, SOUL}); + public static final Aspect MAN = new Aspect("humanus",0xffd7c0, new Aspect[] {BEAST, MIND}); + public static final Aspect CROP = new Aspect("messis",0xe1b371, new Aspect[] {PLANT, MAN}); + public static final Aspect MINE = new Aspect("perfodio",0xdcd2d8, new Aspect[] {MAN, EARTH}); + public static final Aspect TOOL = new Aspect("instrumentum",0x4040ee, new Aspect[] {MAN, ORDER}); + public static final Aspect HARVEST = new Aspect("meto",0xeead82, new Aspect[] {CROP, TOOL}); + public static final Aspect WEAPON = new Aspect("telum",0xc05050, new Aspect[] {TOOL, ENTROPY}); + public static final Aspect ARMOR = new Aspect("tutamen",0x00c0c0, new Aspect[] {TOOL, EARTH}); + public static final Aspect HUNGER = new Aspect("fames",0x9a0305, new Aspect[] {LIFE, VOID}); + public static final Aspect GREED = new Aspect("lucrum",0xe6be44, new Aspect[] {MAN, HUNGER}); + public static final Aspect CRAFT = new Aspect("fabrico",0x809d80, new Aspect[] {MAN, TOOL}); + public static final Aspect CLOTH = new Aspect("pannus",0xeaeac2, new Aspect[] {TOOL, BEAST}); + public static final Aspect MECHANISM = new Aspect("machina",0x8080a0, new Aspect[] {MOTION, TOOL}); + public static final Aspect TRAP = new Aspect("vinculum",0x9a8080, new Aspect[] {MOTION, ENTROPY}); + + +} diff --git a/src/api/java/thaumcraft/api/aspects/AspectList.java b/src/api/java/thaumcraft/api/aspects/AspectList.java new file mode 100644 index 0000000..6b9dfce --- /dev/null +++ b/src/api/java/thaumcraft/api/aspects/AspectList.java @@ -0,0 +1,256 @@ +package thaumcraft.api.aspects; + +import java.io.Serializable; +import java.util.LinkedHashMap; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTTagList; +import thaumcraft.api.ThaumcraftApiHelper; + +public class AspectList implements Serializable { + + public LinkedHashMap aspects = new LinkedHashMap();//aspects associated with this object + + + /** + * this creates a new aspect list with preloaded values based off the aspects of the given item. + * @param the itemstack of the given item + */ + public AspectList(ItemStack stack) { + try { + AspectList temp = ThaumcraftApiHelper.getObjectAspects(stack); + if (temp!=null) + for (Aspect tag:temp.getAspects()) { + add(tag,temp.getAmount(tag)); + } + } catch (Exception e) {} + } + + public AspectList() { + } + + public AspectList copy() { + AspectList out = new AspectList(); + for (Aspect a:this.getAspects()) + out.add(a, this.getAmount(a)); + return out; + } + + /** + * @return the amount of different aspects in this collection + */ + public int size() { + return aspects.size(); + } + + /** + * @return the amount of total vis in this collection + */ + public int visSize() { + int q = 0; + + for (Aspect as:aspects.keySet()) { + q+=this.getAmount(as); + } + + return q; + } + + /** + * @return an array of all the aspects in this collection + */ + public Aspect[] getAspects() { + Aspect[] q = new Aspect[1]; + return aspects.keySet().toArray(q); + } + + /** + * @return an array of all the aspects in this collection + */ + public Aspect[] getPrimalAspects() { + AspectList t = new AspectList(); + for (Aspect as:aspects.keySet()) { + if (as.isPrimal()) { + t.add(as,1); + } + } + Aspect[] q = new Aspect[1]; + return t.aspects.keySet().toArray(q); + } + + /** + * @return an array of all the aspects in this collection sorted by name + */ + public Aspect[] getAspectsSorted() { + try { + Aspect[] out = aspects.keySet().toArray(new Aspect[1]); + boolean change=false; + do { + change=false; + for(int a=0;a0) { + out[a] = e2; + out[a+1] = e1; + change = true; + break; + } + } + } while (change==true); + return out; + } catch (Exception e) { + return this.getAspects(); + } + } + + /** + * @return an array of all the aspects in this collection sorted by amount + */ + public Aspect[] getAspectsSortedAmount() { + try { + Aspect[] out = aspects.keySet().toArray(new Aspect[1]); + boolean change=false; + do { + change=false; + for(int a=0;a0 && e2>0 && e2>e1) { + Aspect ea = out[a]; + Aspect eb = out[a+1]; + out[a] = eb; + out[a+1] = ea; + change = true; + break; + } + } + } while (change==true); + return out; + } catch (Exception e) { + return this.getAspects(); + } + } + + /** + * @param key + * @return the amount associated with the given aspect in this collection + */ + public int getAmount(Aspect key) { + return aspects.get(key)==null?0:aspects.get(key); + } + + /** + * Reduces the amount of an aspect in this collection by the given amount. + * @param key + * @param amount + * @return + */ + public boolean reduce(Aspect key, int amount) { + if (getAmount(key)>=amount) { + int am = getAmount(key)-amount; + aspects.put(key, am); + return true; + } + return false; + } + + /** + * Reduces the amount of an aspect in this collection by the given amount. + * If reduced to 0 or less the aspect will be removed completely. + * @param key + * @param amount + * @return + */ + public AspectList remove(Aspect key, int amount) { + int am = getAmount(key)-amount; + if (am<=0) aspects.remove(key); else + this.aspects.put(key, am); + return this; + } + + /** + * Simply removes the aspect from the list + * @param key + * @param amount + * @return + */ + public AspectList remove(Aspect key) { + aspects.remove(key); + return this; + } + + /** + * Adds this aspect and amount to the collection. + * If the aspect exists then its value will be increased by the given amount. + * @param aspect + * @param amount + * @return + */ + public AspectList add(Aspect aspect, int amount) { + if (this.aspects.containsKey(aspect)) { + int oldamount = this.aspects.get(aspect); + amount+=oldamount; + } + this.aspects.put( aspect, amount ); + return this; + } + + + /** + * Adds this aspect and amount to the collection. + * If the aspect exists then only the highest of the old or new amount will be used. + * @param aspect + * @param amount + * @return + */ + public AspectList merge(Aspect aspect, int amount) { + if (this.aspects.containsKey(aspect)) { + int oldamount = this.aspects.get(aspect); + if (amount0?aspects:null; + } + return null; + } + + @Override + public void setAspects(ItemStack itemstack, AspectList aspects) { + if (!itemstack.hasTagCompound()) itemstack.setTagCompound(new NBTTagCompound()); + aspects.writeToNBT(itemstack.getTagCompound()); + } +*/ \ No newline at end of file diff --git a/src/api/java/thaumcraft/api/aspects/IEssentiaTransport.java b/src/api/java/thaumcraft/api/aspects/IEssentiaTransport.java new file mode 100644 index 0000000..fecbc16 --- /dev/null +++ b/src/api/java/thaumcraft/api/aspects/IEssentiaTransport.java @@ -0,0 +1,100 @@ +package thaumcraft.api.aspects; + +import net.minecraftforge.common.util.ForgeDirection; + + +/** + * @author Azanor + * This interface is used by tiles that use or transport vis. + * Only tiles that implement this interface will be able to connect to vis conduits or other thaumic devices + */ +public interface IEssentiaTransport { + /** + * Is this tile able to connect to other vis users/sources on the specified side? + * @param face + * @return + */ + public boolean isConnectable(ForgeDirection face); + + /** + * Is this side used to input essentia? + * @param face + * @return + */ + boolean canInputFrom(ForgeDirection face); + + /** + * Is this side used to output essentia? + * @param face + * @return + */ + boolean canOutputTo(ForgeDirection face); + + /** + * Sets the amount of suction this block will apply + * @param suction + */ + public void setSuction(Aspect aspect, int amount); + + /** + * Returns the type of suction this block is applying. + * @param loc + * the location from where the suction is being checked + * @return + * a return type of null indicates the suction is untyped and the first thing available will be drawn + */ + public Aspect getSuctionType(ForgeDirection face); + + /** + * Returns the strength of suction this block is applying. + * @param loc + * the location from where the suction is being checked + * @return + */ + public int getSuctionAmount(ForgeDirection face); + + /** + * remove the specified amount of essentia from this transport tile + * @return how much was actually taken + */ + public int takeEssentia(Aspect aspect, int amount, ForgeDirection face); + + /** + * add the specified amount of essentia to this transport tile + * @return how much was actually added + */ + public int addEssentia(Aspect aspect, int amount, ForgeDirection face); + + /** + * What type of essentia this contains + * @param face + * @return + */ + public Aspect getEssentiaType(ForgeDirection face); + + /** + * How much essentia this block contains + * @param face + * @return + */ + public int getEssentiaAmount(ForgeDirection face); + + + + /** + * Essentia will not be drawn from this container unless the suction exceeds this amount. + * @return the amount + */ + public int getMinimumSuction(); + + /** + * Return true if you want the conduit to extend a little further into the block. + * Used by jars and alembics that have smaller than normal hitboxes + * @return + */ + boolean renderExtendedTube(); + + + +} + diff --git a/src/api/java/thaumcraft/api/crafting/CrucibleRecipe.java b/src/api/java/thaumcraft/api/crafting/CrucibleRecipe.java new file mode 100644 index 0000000..5fa968c --- /dev/null +++ b/src/api/java/thaumcraft/api/crafting/CrucibleRecipe.java @@ -0,0 +1,103 @@ +package thaumcraft.api.crafting; + +import java.util.ArrayList; + +import net.minecraft.item.ItemStack; +import net.minecraftforge.oredict.OreDictionary; +import thaumcraft.api.ThaumcraftApiHelper; +import thaumcraft.api.aspects.Aspect; +import thaumcraft.api.aspects.AspectList; + +public class CrucibleRecipe { + + private ItemStack recipeOutput; + + public Object catalyst; + public AspectList aspects; + public String key; + + public int hash; + + public CrucibleRecipe(String researchKey, ItemStack result, Object cat, AspectList tags) { + recipeOutput = result; + this.aspects = tags; + this.key = researchKey; + this.catalyst = cat; + if (cat instanceof String) { + this.catalyst = OreDictionary.getOres((String) cat); + } + String hc = researchKey + result.toString(); + for (Aspect tag:tags.getAspects()) { + hc += tag.getTag()+tags.getAmount(tag); + } + hc += cat.hashCode(); + hash = hc.hashCode(); + } + + + + public boolean matches(AspectList itags, ItemStack cat) { + if (catalyst instanceof ItemStack && + !ThaumcraftApiHelper.itemMatches((ItemStack) catalyst,cat,false)) { + return false; + } else + if (catalyst instanceof ArrayList && ((ArrayList)catalyst).size()>0) { + ItemStack[] ores = ((ArrayList)catalyst).toArray(new ItemStack[]{}); + if (!ThaumcraftApiHelper.containsMatch(false, new ItemStack[]{cat},ores)) return false; + } + if (itags==null) return false; + for (Aspect tag:aspects.getAspects()) { + if (itags.getAmount(tag))catalyst).size()>0) { + ItemStack[] ores = ((ArrayList)catalyst).toArray(new ItemStack[]{}); + if (ThaumcraftApiHelper.containsMatch(false, new ItemStack[]{cat},ores)) return true; + } + return false; + } + + public AspectList removeMatching(AspectList itags) { + AspectList temptags = new AspectList(); + temptags.aspects.putAll(itags.aspects); + + for (Aspect tag:aspects.getAspects()) { + temptags.remove(tag, aspects.getAmount(tag)); +// if (!temptags.remove(tag, aspects.getAmount(tag))) return null; + } + + itags = temptags; + return itags; + } + + public ItemStack getRecipeOutput() { + return recipeOutput; + } + + +// @Override +// public int hashCode() { +// String hash = ""; +// if (catalyst instanceof ItemStack) { +// hash += ((ItemStack)catalyst).toString(); +// } else if (catalyst instanceof ArrayList && ((ArrayList)catalyst).size()>0) { +// for (ItemStack s:(ArrayList)catalyst) { +// hash += s.toString(); +// } +// } else { +// hash += catalyst.hashCode(); +// } +// hash += getRecipeOutput().toString(); +// for (Aspect a:aspects.getAspectsSorted()) { +// hash += a.getTag() + aspects.getAmount(a); +// } +// return hash.hashCode(); +// } + +} diff --git a/src/api/java/thaumcraft/api/crafting/IArcaneRecipe.java b/src/api/java/thaumcraft/api/crafting/IArcaneRecipe.java new file mode 100644 index 0000000..bb5036d --- /dev/null +++ b/src/api/java/thaumcraft/api/crafting/IArcaneRecipe.java @@ -0,0 +1,35 @@ +package thaumcraft.api.crafting; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import thaumcraft.api.aspects.AspectList; + +public interface IArcaneRecipe +{ + + + /** + * Used to check if a recipe matches current crafting inventory + * @param player + */ + boolean matches(IInventory var1, World world, EntityPlayer player); + + /** + * Returns an Item that is the result of this recipe + */ + ItemStack getCraftingResult(IInventory var1); + + /** + * Returns the size of the recipe area + */ + int getRecipeSize(); + + ItemStack getRecipeOutput(); + AspectList getAspects(); + AspectList getAspects(IInventory var1); + String getResearch(); + + +} diff --git a/src/api/java/thaumcraft/api/crafting/IInfusionStabiliser.java b/src/api/java/thaumcraft/api/crafting/IInfusionStabiliser.java new file mode 100644 index 0000000..d137ff2 --- /dev/null +++ b/src/api/java/thaumcraft/api/crafting/IInfusionStabiliser.java @@ -0,0 +1,19 @@ +package thaumcraft.api.crafting; + +import net.minecraft.world.World; + +/** + * + * @author Azanor + * + * Blocks that implement this interface act as infusion crafting stabilisers like candles and skulls + * + */ +public interface IInfusionStabiliser { + + /** + * returns true if the block can stabilise things + */ + public boolean canStabaliseInfusion(World world, int x, int y, int z); + +} diff --git a/src/api/java/thaumcraft/api/crafting/InfusionEnchantmentRecipe.java b/src/api/java/thaumcraft/api/crafting/InfusionEnchantmentRecipe.java new file mode 100644 index 0000000..6fb4d63 --- /dev/null +++ b/src/api/java/thaumcraft/api/crafting/InfusionEnchantmentRecipe.java @@ -0,0 +1,156 @@ +package thaumcraft.api.crafting; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Map; + +import net.minecraft.enchantment.Enchantment; +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.oredict.OreDictionary; +import thaumcraft.api.ThaumcraftApiHelper; +import thaumcraft.api.aspects.AspectList; + +public class InfusionEnchantmentRecipe +{ + + public AspectList aspects; + public String research; + public ItemStack[] components; + public Enchantment enchantment; + public int recipeXP; + public int instability; + + public InfusionEnchantmentRecipe(String research, Enchantment input, int inst, + AspectList aspects2, ItemStack[] recipe) { + this.research = research; + this.enchantment = input; + this.aspects = aspects2; + this.components = recipe; + this.instability = inst; + this.recipeXP = Math.max(1, input.getMinEnchantability(1)/3); + } + + /** + * Used to check if a recipe matches current crafting inventory + * @param player + */ + public boolean matches(ArrayList input, ItemStack central, World world, EntityPlayer player) { + if (research.length()>0 && !ThaumcraftApiHelper.isResearchComplete(player.getCommandSenderName(), research)) { + return false; + } + + if (!enchantment.canApply(central) || !central.getItem().isItemTool(central)) { + return false; + } + + Map map1 = EnchantmentHelper.getEnchantments(central); + Iterator iterator = map1.keySet().iterator(); + while (iterator.hasNext()) + { + int j1 = ((Integer)iterator.next()).intValue(); + Enchantment ench = Enchantment.enchantmentsList[j1]; + if (j1 == enchantment.effectId && + EnchantmentHelper.getEnchantmentLevel(j1, central)>=ench.getMaxLevel()) + return false; + if (enchantment.effectId != ench.effectId && + (!enchantment.canApplyTogether(ench) || + !ench.canApplyTogether(enchantment))) { + return false; + } + } + + ItemStack i2 = null; + + ArrayList ii = new ArrayList(); + for (ItemStack is:input) { + ii.add(is.copy()); + } + + for (ItemStack comp:components) { + boolean b=false; + for (int a=0;a stack0.getMaxStackSize() ? false : t1)); + } + + + public Enchantment getEnchantment() { + return enchantment; + + } + + public AspectList getAspects() { + return aspects; + + } + + public String getResearch() { + return research; + + } + + public int calcInstability(ItemStack recipeInput) { + int i = 0; + Map map1 = EnchantmentHelper.getEnchantments(recipeInput); + Iterator iterator = map1.keySet().iterator(); + while (iterator.hasNext()) + { + int j1 = ((Integer)iterator.next()).intValue(); + i += EnchantmentHelper.getEnchantmentLevel(j1, recipeInput); + } + return (i/2) + instability; + } + + public int calcXP(ItemStack recipeInput) { + return recipeXP * (1+EnchantmentHelper.getEnchantmentLevel(enchantment.effectId, recipeInput)); + } + + public float getEssentiaMod(ItemStack recipeInput) { + float mod = EnchantmentHelper.getEnchantmentLevel(enchantment.effectId, recipeInput); + Map map1 = EnchantmentHelper.getEnchantments(recipeInput); + Iterator iterator = map1.keySet().iterator(); + while (iterator.hasNext()) + { + int j1 = ((Integer)iterator.next()).intValue(); + if (j1 != enchantment.effectId) + mod += EnchantmentHelper.getEnchantmentLevel(j1, recipeInput) * .1f; + } + return mod; + } + +} diff --git a/src/api/java/thaumcraft/api/crafting/InfusionRecipe.java b/src/api/java/thaumcraft/api/crafting/InfusionRecipe.java new file mode 100644 index 0000000..fef0a34 --- /dev/null +++ b/src/api/java/thaumcraft/api/crafting/InfusionRecipe.java @@ -0,0 +1,128 @@ +package thaumcraft.api.crafting; + +import java.util.ArrayList; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.oredict.OreDictionary; +import thaumcraft.api.ThaumcraftApiHelper; +import thaumcraft.api.aspects.AspectList; + +public class InfusionRecipe +{ + protected AspectList aspects; + protected String research; + private ItemStack[] components; + private ItemStack recipeInput; + protected Object recipeOutput; + protected int instability; + + public InfusionRecipe(String research, Object output, int inst, + AspectList aspects2, ItemStack input, ItemStack[] recipe) { + this.research = research; + this.recipeOutput = output; + this.recipeInput = input; + this.aspects = aspects2; + this.components = recipe; + this.instability = inst; + } + + /** + * Used to check if a recipe matches current crafting inventory + * @param player + */ + public boolean matches(ArrayList input, ItemStack central, World world, EntityPlayer player) { + if (getRecipeInput()==null) return false; + + if (research.length()>0 && !ThaumcraftApiHelper.isResearchComplete(player.getCommandSenderName(), research)) { + return false; + } + + ItemStack i2 = central.copy(); + if (getRecipeInput().getItemDamage()==OreDictionary.WILDCARD_VALUE) { + i2.setItemDamage(OreDictionary.WILDCARD_VALUE); + } + + if (!areItemStacksEqual(i2, getRecipeInput(), true)) return false; + + ArrayList ii = new ArrayList(); + for (ItemStack is:input) { + ii.add(is.copy()); + } + + for (ItemStack comp:getComponents()) { + boolean b=false; + for (int a=0;a stack0.getMaxStackSize() ? false : t1)); + } + + + public Object getRecipeOutput() { + return getRecipeOutput(this.getRecipeInput()); + } + + public AspectList getAspects() { + return getAspects(this.getRecipeInput()); + } + + public int getInstability() { + return getInstability(this.getRecipeInput()); + } + + public String getResearch() { + return research; + } + + public ItemStack getRecipeInput() { + return recipeInput; + } + + public ItemStack[] getComponents() { + return components; + } + + public Object getRecipeOutput(ItemStack input) { + return recipeOutput; + } + + public AspectList getAspects(ItemStack input) { + return aspects; + } + + public int getInstability(ItemStack input) { + return instability; + } +} diff --git a/src/api/java/thaumcraft/api/crafting/ShapedArcaneRecipe.java b/src/api/java/thaumcraft/api/crafting/ShapedArcaneRecipe.java new file mode 100644 index 0000000..40c7bc2 --- /dev/null +++ b/src/api/java/thaumcraft/api/crafting/ShapedArcaneRecipe.java @@ -0,0 +1,261 @@ +package thaumcraft.api.crafting; + +import java.util.ArrayList; +import java.util.HashMap; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.oredict.OreDictionary; +import thaumcraft.api.ThaumcraftApiHelper; +import thaumcraft.api.aspects.AspectList; + +public class ShapedArcaneRecipe implements IArcaneRecipe +{ + //Added in for future ease of change, but hard coded for now. + private static final int MAX_CRAFT_GRID_WIDTH = 3; + private static final int MAX_CRAFT_GRID_HEIGHT = 3; + + public ItemStack output = null; + public Object[] input = null; + public AspectList aspects = null; + public String research; + public int width = 0; + public int height = 0; + private boolean mirrored = true; + + public ShapedArcaneRecipe(String research, Block result, AspectList aspects, Object... recipe){ this(research, new ItemStack(result), aspects, recipe); } + public ShapedArcaneRecipe(String research, Item result, AspectList aspects, Object... recipe){ this(research, new ItemStack(result), aspects, recipe); } + public ShapedArcaneRecipe(String research, ItemStack result, AspectList aspects, Object... recipe) + { + output = result.copy(); + this.research = research; + this.aspects = aspects; + String shape = ""; + + int idx = 0; + + if (recipe[idx] instanceof Boolean) + { + mirrored = (Boolean)recipe[idx]; + if (recipe[idx+1] instanceof Object[]) + { + recipe = (Object[])recipe[idx+1]; + } + else + { + idx = 1; + } + } + + if (recipe[idx] instanceof String[]) + { + String[] parts = ((String[])recipe[idx++]); + + for (String s : parts) + { + width = s.length(); + shape += s; + } + + height = parts.length; + } + else + { + while (recipe[idx] instanceof String) + { + String s = (String)recipe[idx++]; + shape += s; + width = s.length(); + height++; + } + } + + if (width * height != shape.length()) + { + String ret = "Invalid shaped ore recipe: "; + for (Object tmp : recipe) + { + ret += tmp + ", "; + } + ret += output; + throw new RuntimeException(ret); + } + + HashMap itemMap = new HashMap(); + + for (; idx < recipe.length; idx += 2) + { + Character chr = (Character)recipe[idx]; + Object in = recipe[idx + 1]; + + if (in instanceof ItemStack) + { + itemMap.put(chr, ((ItemStack)in).copy()); + } + else if (in instanceof Item) + { + itemMap.put(chr, new ItemStack((Item)in)); + } + else if (in instanceof Block) + { + itemMap.put(chr, new ItemStack((Block)in, 1, OreDictionary.WILDCARD_VALUE)); + } + else if (in instanceof String) + { + itemMap.put(chr, OreDictionary.getOres((String)in)); + } + else + { + String ret = "Invalid shaped ore recipe: "; + for (Object tmp : recipe) + { + ret += tmp + ", "; + } + ret += output; + throw new RuntimeException(ret); + } + } + + input = new Object[width * height]; + int x = 0; + for (char chr : shape.toCharArray()) + { + input[x++] = itemMap.get(chr); + } + } + + @Override + public ItemStack getCraftingResult(IInventory var1){ return output.copy(); } + + @Override + public int getRecipeSize(){ return input.length; } + + @Override + public ItemStack getRecipeOutput(){ return output; } + + @Override + public boolean matches(IInventory inv, World world, EntityPlayer player) + { + if (research.length()>0 && !ThaumcraftApiHelper.isResearchComplete(player.getCommandSenderName(), research)) { + return false; + } + for (int x = 0; x <= MAX_CRAFT_GRID_WIDTH - width; x++) + { + for (int y = 0; y <= MAX_CRAFT_GRID_HEIGHT - height; ++y) + { + if (checkMatch(inv, x, y, false)) + { + return true; + } + + if (mirrored && checkMatch(inv, x, y, true)) + { + return true; + } + } + } + + return false; + } + + private boolean checkMatch(IInventory inv, int startX, int startY, boolean mirror) + { + for (int x = 0; x < MAX_CRAFT_GRID_WIDTH; x++) + { + for (int y = 0; y < MAX_CRAFT_GRID_HEIGHT; y++) + { + int subX = x - startX; + int subY = y - startY; + Object target = null; + + if (subX >= 0 && subY >= 0 && subX < width && subY < height) + { + if (mirror) + { + target = input[width - subX - 1 + subY * width]; + } + else + { + target = input[subX + subY * width]; + } + } + + ItemStack slot = ThaumcraftApiHelper.getStackInRowAndColumn(inv, x, y); + + if (target instanceof ItemStack) + { + if (!checkItemEquals((ItemStack)target, slot)) + { + return false; + } + } + else if (target instanceof ArrayList) + { + boolean matched = false; + + for (ItemStack item : (ArrayList)target) + { + matched = matched || checkItemEquals(item, slot); + } + + if (!matched) + { + return false; + } + } + else if (target == null && slot != null) + { + return false; + } + } + } + + return true; + } + + private boolean checkItemEquals(ItemStack target, ItemStack input) + { + if (input == null && target != null || input != null && target == null) + { + return false; + } + return (target.getItem() == input.getItem() && + (!target.hasTagCompound() || ItemStack.areItemStackTagsEqual(target, input)) && + (target.getItemDamage() == OreDictionary.WILDCARD_VALUE|| target.getItemDamage() == input.getItemDamage())); + } + + public ShapedArcaneRecipe setMirrored(boolean mirror) + { + mirrored = mirror; + return this; + } + + /** + * Returns the input for this recipe, any mod accessing this value should never + * manipulate the values in this array as it will effect the recipe itself. + * @return The recipes input vales. + */ + public Object[] getInput() + { + return this.input; + } + + @Override + public AspectList getAspects() { + return aspects; + } + + @Override + public AspectList getAspects(IInventory inv) { + return aspects; + } + + @Override + public String getResearch() { + return research; + } +} diff --git a/src/api/java/thaumcraft/api/crafting/ShapelessArcaneRecipe.java b/src/api/java/thaumcraft/api/crafting/ShapelessArcaneRecipe.java new file mode 100644 index 0000000..3d4ed50 --- /dev/null +++ b/src/api/java/thaumcraft/api/crafting/ShapelessArcaneRecipe.java @@ -0,0 +1,157 @@ +package thaumcraft.api.crafting; + +import java.util.ArrayList; +import java.util.Iterator; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; +import net.minecraftforge.oredict.OreDictionary; +import thaumcraft.api.ThaumcraftApiHelper; +import thaumcraft.api.aspects.AspectList; + +public class ShapelessArcaneRecipe implements IArcaneRecipe +{ + private ItemStack output = null; + private ArrayList input = new ArrayList(); + + public AspectList aspects = null; + public String research; + + public ShapelessArcaneRecipe(String research, Block result, AspectList aspects, Object... recipe){ this(research,new ItemStack(result),aspects, recipe); } + public ShapelessArcaneRecipe(String research, Item result, AspectList aspects, Object... recipe){ this(research,new ItemStack(result),aspects, recipe); } + + public ShapelessArcaneRecipe(String research, ItemStack result, AspectList aspects, Object... recipe) + { + output = result.copy(); + this.research = research; + this.aspects = aspects; + for (Object in : recipe) + { + if (in instanceof ItemStack) + { + input.add(((ItemStack)in).copy()); + } + else if (in instanceof Item) + { + input.add(new ItemStack((Item)in)); + } + else if (in instanceof Block) + { + input.add(new ItemStack((Block)in)); + } + else if (in instanceof String) + { + input.add(OreDictionary.getOres((String)in)); + } + else + { + String ret = "Invalid shapeless ore recipe: "; + for (Object tmp : recipe) + { + ret += tmp + ", "; + } + ret += output; + throw new RuntimeException(ret); + } + } + } + + @Override + public int getRecipeSize(){ return input.size(); } + + @Override + public ItemStack getRecipeOutput(){ return output; } + + @Override + public ItemStack getCraftingResult(IInventory var1){ return output.copy(); } + + @Override + public boolean matches(IInventory var1, World world, EntityPlayer player) + { + if (research.length()>0 && !ThaumcraftApiHelper.isResearchComplete(player.getCommandSenderName(), research)) { + return false; + } + + ArrayList required = new ArrayList(input); + + for (int x = 0; x < 9; x++) + { + ItemStack slot = var1.getStackInSlot(x); + + if (slot != null) + { + boolean inRecipe = false; + Iterator req = required.iterator(); + + while (req.hasNext()) + { + boolean match = false; + + Object next = req.next(); + + if (next instanceof ItemStack) + { + match = checkItemEquals((ItemStack)next, slot); + } + else if (next instanceof ArrayList) + { + for (ItemStack item : (ArrayList)next) + { + match = match || checkItemEquals(item, slot); + } + } + + if (match) + { + inRecipe = true; + required.remove(next); + break; + } + } + + if (!inRecipe) + { + return false; + } + } + } + + return required.isEmpty(); + } + + private boolean checkItemEquals(ItemStack target, ItemStack input) + { + return (target.getItem() == input.getItem() && + (!target.hasTagCompound() || ItemStack.areItemStackTagsEqual(target, input)) && + (target.getItemDamage() == OreDictionary.WILDCARD_VALUE || target.getItemDamage() == input.getItemDamage())); + } + + /** + * Returns the input for this recipe, any mod accessing this value should never + * manipulate the values in this array as it will effect the recipe itself. + * @return The recipes input vales. + */ + public ArrayList getInput() + { + return this.input; + } + + @Override + public AspectList getAspects() { + return aspects; + } + + @Override + public AspectList getAspects(IInventory inv) { + return aspects; + } + + @Override + public String getResearch() { + return research; + } +} diff --git a/src/api/java/thaumcraft/api/damagesource/DamageSourceIndirectThaumcraftEntity.java b/src/api/java/thaumcraft/api/damagesource/DamageSourceIndirectThaumcraftEntity.java new file mode 100644 index 0000000..1562d05 --- /dev/null +++ b/src/api/java/thaumcraft/api/damagesource/DamageSourceIndirectThaumcraftEntity.java @@ -0,0 +1,32 @@ +package thaumcraft.api.damagesource; + +import net.minecraft.entity.Entity; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EntityDamageSourceIndirect; + +public class DamageSourceIndirectThaumcraftEntity extends EntityDamageSourceIndirect { + + private boolean fireDamage; + private float hungerDamage; + private boolean isUnblockable; + + + public DamageSourceIndirectThaumcraftEntity(String par1Str, + Entity par2Entity, Entity par3Entity) { + super(par1Str, par2Entity, par3Entity); + } + + + public DamageSource setFireDamage() + { + this.fireDamage = true; + return this; + } + + public DamageSource setDamageBypassesArmor() + { + this.isUnblockable = true; + this.hungerDamage = 0.0F; + return this; + } +} diff --git a/src/api/java/thaumcraft/api/damagesource/DamageSourceThaumcraft.java b/src/api/java/thaumcraft/api/damagesource/DamageSourceThaumcraft.java new file mode 100644 index 0000000..bb55672 --- /dev/null +++ b/src/api/java/thaumcraft/api/damagesource/DamageSourceThaumcraft.java @@ -0,0 +1,47 @@ +package thaumcraft.api.damagesource; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.util.DamageSource; +import net.minecraft.util.EntityDamageSource; + +public class DamageSourceThaumcraft extends DamageSource +{ + + public static DamageSource taint = new DamageSourceThaumcraft("taint").setDamageBypassesArmor().setMagicDamage(); + public static DamageSource tentacle = new DamageSourceThaumcraft("tentacle"); + public static DamageSource swarm = new DamageSourceThaumcraft("swarm"); + public static DamageSource dissolve = new DamageSourceThaumcraft("dissolve").setDamageBypassesArmor(); + + protected DamageSourceThaumcraft(String par1Str) { + super(par1Str); + } + + /** This kind of damage can be blocked or not. */ + private boolean isUnblockable = false; + private boolean isDamageAllowedInCreativeMode = false; + private float hungerDamage = 0.3F; + + /** This kind of damage is based on fire or not. */ + private boolean fireDamage; + + /** This kind of damage is based on a projectile or not. */ + private boolean projectile; + + /** + * Whether this damage source will have its damage amount scaled based on the current difficulty. + */ + private boolean difficultyScaled; + private boolean magicDamage = false; + private boolean explosion = false; + + public static DamageSource causeSwarmDamage(EntityLivingBase par0EntityLiving) + { + return new EntityDamageSource("swarm", par0EntityLiving); + } + + public static DamageSource causeTentacleDamage(EntityLivingBase par0EntityLiving) + { + return new EntityDamageSource("tentacle", par0EntityLiving); + } + +} diff --git a/src/api/java/thaumcraft/api/entities/ITaintedMob.java b/src/api/java/thaumcraft/api/entities/ITaintedMob.java new file mode 100644 index 0000000..83fb1fc --- /dev/null +++ b/src/api/java/thaumcraft/api/entities/ITaintedMob.java @@ -0,0 +1,5 @@ +package thaumcraft.api.entities; + +public interface ITaintedMob { + +} diff --git a/src/api/java/thaumcraft/api/nodes/INode.java b/src/api/java/thaumcraft/api/nodes/INode.java new file mode 100644 index 0000000..8c71414 --- /dev/null +++ b/src/api/java/thaumcraft/api/nodes/INode.java @@ -0,0 +1,53 @@ +package thaumcraft.api.nodes; + +import thaumcraft.api.aspects.Aspect; +import thaumcraft.api.aspects.AspectList; +import thaumcraft.api.aspects.IAspectContainer; + +public interface INode extends IAspectContainer { + + /** + * Unique identifier to distinguish nodes. Normal node id's are based on world id and coordinates + * @return + */ + public String getId(); + + public AspectList getAspectsBase(); + + /** + * Return the type of node + * @return + */ + public NodeType getNodeType(); + + /** + * Set the type of node + * @return + */ + public void setNodeType(NodeType nodeType); + + /** + * Return the node modifier + * @return + */ + public void setNodeModifier(NodeModifier nodeModifier); + + /** + * Set the node modifier + * @return + */ + public NodeModifier getNodeModifier(); + + /** + * Return the maximum capacity of each aspect the node can hold + * @return + */ + public int getNodeVisBase(Aspect aspect); + + /** + * Set the maximum capacity of each aspect the node can hold + * @return + */ + public void setNodeVisBase(Aspect aspect, short nodeVisBase); + +} diff --git a/src/api/java/thaumcraft/api/nodes/IRevealer.java b/src/api/java/thaumcraft/api/nodes/IRevealer.java new file mode 100644 index 0000000..14a19b5 --- /dev/null +++ b/src/api/java/thaumcraft/api/nodes/IRevealer.java @@ -0,0 +1,22 @@ +package thaumcraft.api.nodes; + +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.item.ItemStack; + +/** + * + * @author Azanor + * + * Equipped head slot items that extend this class will make nodes visible in world. + * + */ + +public interface IRevealer { + + /* + * If this method returns true the nodes will be visible. + */ + public boolean showNodes(ItemStack itemstack, EntityLivingBase player); + + +} diff --git a/src/api/java/thaumcraft/api/nodes/NodeModifier.java b/src/api/java/thaumcraft/api/nodes/NodeModifier.java new file mode 100644 index 0000000..885b867 --- /dev/null +++ b/src/api/java/thaumcraft/api/nodes/NodeModifier.java @@ -0,0 +1,6 @@ +package thaumcraft.api.nodes; + +public enum NodeModifier +{ + BRIGHT, PALE, FADING +} \ No newline at end of file diff --git a/src/api/java/thaumcraft/api/nodes/NodeType.java b/src/api/java/thaumcraft/api/nodes/NodeType.java new file mode 100644 index 0000000..355324b --- /dev/null +++ b/src/api/java/thaumcraft/api/nodes/NodeType.java @@ -0,0 +1,6 @@ +package thaumcraft.api.nodes; + +public enum NodeType +{ + NORMAL, UNSTABLE, DARK, TAINTED, HUNGRY, PURE +} \ No newline at end of file diff --git a/src/api/java/thaumcraft/api/package-info.java b/src/api/java/thaumcraft/api/package-info.java new file mode 100644 index 0000000..0de3926 --- /dev/null +++ b/src/api/java/thaumcraft/api/package-info.java @@ -0,0 +1,4 @@ +@API(owner = "Thaumcraft", apiVersion = "4.2.0.0", provides = "Thaumcraft|API") +package thaumcraft.api; + +import cpw.mods.fml.common.API; diff --git a/src/api/java/thaumcraft/api/potions/PotionFluxTaint.java b/src/api/java/thaumcraft/api/potions/PotionFluxTaint.java new file mode 100644 index 0000000..b950de0 --- /dev/null +++ b/src/api/java/thaumcraft/api/potions/PotionFluxTaint.java @@ -0,0 +1,67 @@ +package thaumcraft.api.potions; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.potion.Potion; +import net.minecraft.util.ResourceLocation; +import thaumcraft.api.damagesource.DamageSourceThaumcraft; +import thaumcraft.api.entities.ITaintedMob; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +public class PotionFluxTaint extends Potion +{ + public static PotionFluxTaint instance = null; // will be instantiated at runtime + private int statusIconIndex = -1; + + public PotionFluxTaint(int par1, boolean par2, int par3) + { + super(par1,par2,par3); + setIconIndex(0, 0); + } + + public static void init() + { + instance.setPotionName("potion.fluxtaint"); + instance.setIconIndex(3, 1); + instance.setEffectiveness(0.25D); + } + + @Override + public boolean isBadEffect() { + return true; + } + + @Override + @SideOnly(Side.CLIENT) + public int getStatusIconIndex() { + Minecraft.getMinecraft().renderEngine.bindTexture(rl); + return super.getStatusIconIndex(); + } + + static final ResourceLocation rl = new ResourceLocation("thaumcraft","textures/misc/potions.png"); + + @Override + public void performEffect(EntityLivingBase target, int par2) { + if (target instanceof ITaintedMob) { + target.heal(1); + } else + if (!target.isEntityUndead() && !(target instanceof EntityPlayer)) + { + target.attackEntityFrom(DamageSourceThaumcraft.taint, 1); + } + else + if (!target.isEntityUndead() && (target.getMaxHealth() > 1 || (target instanceof EntityPlayer))) + { + target.attackEntityFrom(DamageSourceThaumcraft.taint, 1); + } + } + + public boolean isReady(int par1, int par2) + { + int k = 40 >> par2; + return k > 0 ? par1 % k == 0 : true; + } + +} diff --git a/src/api/java/thaumcraft/api/potions/PotionVisExhaust.java b/src/api/java/thaumcraft/api/potions/PotionVisExhaust.java new file mode 100644 index 0000000..cd7fc18 --- /dev/null +++ b/src/api/java/thaumcraft/api/potions/PotionVisExhaust.java @@ -0,0 +1,48 @@ +package thaumcraft.api.potions; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.EntityLivingBase; +import net.minecraft.potion.Potion; +import net.minecraft.util.ResourceLocation; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +public class PotionVisExhaust extends Potion +{ + public static PotionVisExhaust instance = null; // will be instantiated at runtime + private int statusIconIndex = -1; + + public PotionVisExhaust(int par1, boolean par2, int par3) + { + super(par1,par2,par3); + setIconIndex(0, 0); + } + + public static void init() + { + instance.setPotionName("potion.visexhaust"); + instance.setIconIndex(5, 1); + instance.setEffectiveness(0.25D); + } + + @Override + public boolean isBadEffect() { + return true; + } + + @Override + @SideOnly(Side.CLIENT) + public int getStatusIconIndex() { + Minecraft.getMinecraft().renderEngine.bindTexture(rl); + return super.getStatusIconIndex(); + } + + static final ResourceLocation rl = new ResourceLocation("thaumcraft","textures/misc/potions.png"); + + @Override + public void performEffect(EntityLivingBase target, int par2) { + + } + + +} diff --git a/src/api/java/thaumcraft/api/research/IScanEventHandler.java b/src/api/java/thaumcraft/api/research/IScanEventHandler.java new file mode 100644 index 0000000..d0efac5 --- /dev/null +++ b/src/api/java/thaumcraft/api/research/IScanEventHandler.java @@ -0,0 +1,9 @@ +package thaumcraft.api.research; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +public interface IScanEventHandler { + ScanResult scanPhenomena(ItemStack stack, World world, EntityPlayer player); +} diff --git a/src/api/java/thaumcraft/api/research/ResearchCategories.java b/src/api/java/thaumcraft/api/research/ResearchCategories.java new file mode 100644 index 0000000..82309b3 --- /dev/null +++ b/src/api/java/thaumcraft/api/research/ResearchCategories.java @@ -0,0 +1,101 @@ +package thaumcraft.api.research; + +import java.util.Collection; +import java.util.LinkedHashMap; + +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; + +import org.apache.logging.log4j.Level; + +import cpw.mods.fml.common.FMLLog; + +public class ResearchCategories { + + //Research + public static LinkedHashMap researchCategories = new LinkedHashMap (); + + /** + * @param key + * @return the research item linked to this key + */ + public static ResearchCategoryList getResearchList(String key) { + return researchCategories.get(key); + } + + /** + * @param key + * @return the name of the research category linked to this key. + * Must be stored as localization information in the LanguageRegistry. + */ + public static String getCategoryName(String key) { + return StatCollector.translateToLocal("tc.research_category."+key); + } + + /** + * @param key the research key + * @return the ResearchItem object. + */ + public static ResearchItem getResearch(String key) { + Collection rc = researchCategories.values(); + for (Object cat:rc) { + Collection rl = ((ResearchCategoryList)cat).research.values(); + for (Object ri:rl) { + if ((((ResearchItem)ri).key).equals(key)) return (ResearchItem)ri; + } + } + return null; + } + + /** + * This should only be done at the PostInit stage + * @param key the key used for this category + * @param icon the icon to be used for the research category tab + * @param background the resource location of the background image to use for this category + * @return the name of the research linked to this key + */ + public static void registerCategory(String key, ResourceLocation icon, ResourceLocation background) { + if (getResearchList(key)==null) { + ResearchCategoryList rl = new ResearchCategoryList(icon, background); + researchCategories.put(key, rl); + } + } + + public static void addResearch(ResearchItem ri) { + ResearchCategoryList rl = getResearchList(ri.category); + if (rl!=null && !rl.research.containsKey(ri.key)) { + + if (!ri.isVirtual()) { + for (ResearchItem rr:rl.research.values()) { + if (rr.displayColumn == ri.displayColumn && rr.displayRow == ri.displayRow) { + FMLLog.log(Level.FATAL, "[Thaumcraft] Research ["+ri.getName()+"] not added as it overlaps with existing research ["+rr.getName()+"]"); + return; + } + } + } + + + rl.research.put(ri.key, ri); + + if (ri.displayColumn < rl.minDisplayColumn) + { + rl.minDisplayColumn = ri.displayColumn; + } + + if (ri.displayRow < rl.minDisplayRow) + { + rl.minDisplayRow = ri.displayRow; + } + + if (ri.displayColumn > rl.maxDisplayColumn) + { + rl.maxDisplayColumn = ri.displayColumn; + } + + if (ri.displayRow > rl.maxDisplayRow) + { + rl.maxDisplayRow = ri.displayRow; + } + } + } +} diff --git a/src/api/java/thaumcraft/api/research/ResearchCategoryList.java b/src/api/java/thaumcraft/api/research/ResearchCategoryList.java new file mode 100644 index 0000000..7eed010 --- /dev/null +++ b/src/api/java/thaumcraft/api/research/ResearchCategoryList.java @@ -0,0 +1,37 @@ +package thaumcraft.api.research; + +import java.util.HashMap; +import java.util.Map; + +import net.minecraft.util.ResourceLocation; + +public class ResearchCategoryList { + + /** Is the smallest column used on the GUI. */ + public int minDisplayColumn; + + /** Is the smallest row used on the GUI. */ + public int minDisplayRow; + + /** Is the biggest column used on the GUI. */ + public int maxDisplayColumn; + + /** Is the biggest row used on the GUI. */ + public int maxDisplayRow; + + /** display variables **/ + public ResourceLocation icon; + public ResourceLocation background; + + public ResearchCategoryList(ResourceLocation icon, ResourceLocation background) { + this.icon = icon; + this.background = background; + } + + //Research + public Map research = new HashMap(); + + + + +} diff --git a/src/api/java/thaumcraft/api/research/ResearchItem.java b/src/api/java/thaumcraft/api/research/ResearchItem.java new file mode 100644 index 0000000..55b3820 --- /dev/null +++ b/src/api/java/thaumcraft/api/research/ResearchItem.java @@ -0,0 +1,367 @@ +package thaumcraft.api.research; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; +import thaumcraft.api.aspects.Aspect; +import thaumcraft.api.aspects.AspectList; + +public class ResearchItem +{ + /** + * A short string used as a key for this research. Must be unique + */ + public final String key; + + /** + * A short string used as a reference to the research category to which this must be added. + */ + public final String category; + + /** + * The aspect tags and their values required to complete this research + */ + public final AspectList tags; + + /** + * This links to any research that needs to be completed before this research can be discovered or learnt. + */ + public String[] parents = null; + + /** + * Like parent above, but a line will not be displayed in the thaumonomicon linking them. Just used to prevent clutter. + */ + public String[] parentsHidden = null; + /** + * any research linked to this that will be unlocked automatically when this research is complete + */ + public String[] siblings = null; + + /** + * the horizontal position of the research icon + */ + public final int displayColumn; + + /** + * the vertical position of the research icon + */ + public final int displayRow; + + /** + * the icon to be used for this research + */ + public final ItemStack icon_item; + + /** + * the icon to be used for this research + */ + public final ResourceLocation icon_resource; + + /** + * How large the research grid is. Valid values are 1 to 3. + */ + private int complexity; + + /** + * Special research has a spiky border. Used for important research milestones. + */ + private boolean isSpecial; + + /** + * Research that can be directly purchased with RP in normal research difficulty. + */ + private boolean isSecondary; + + /** + * This indicates if the research should use a circular icon border. Usually used for "passive" research + * that doesn't have recipes and grants passive effects, or that unlock automatically. + */ + private boolean isRound; + + /** + * Stub research cannot be discovered by normal means, but can be unlocked via the sibling system. + */ + private boolean isStub; + + /** + * This indicated that the research is completely hidden and cannot be discovered by any + * player-controlled means. The recipes will never show up in the thaumonomicon. + * Usually used to unlock "hidden" recipes via sibling unlocking, like + * the various cap and rod combos for wands. + */ + private boolean isVirtual; + + @Deprecated + private boolean isLost; + + /** + * Concealed research does not display in the thaumonomicon until parent researches are discovered. + */ + private boolean isConcealed; + + /** + * Hidden research can only be discovered via scanning or knowledge fragments + */ + private boolean isHidden; + + /** + * These research items will automatically unlock for all players on game start + */ + private boolean isAutoUnlock; + + /** + * Scanning these items will have a chance of revealing hidden knowledge in the thaumonomicon + */ + private ItemStack[] itemTriggers; + + /** + * Scanning these entities will have a chance of revealing hidden knowledge in the thaumonomicon + */ + private String[] entityTriggers; + + /** + * Scanning things with these aspects will have a chance of revealing hidden knowledge in the thaumonomicon + */ + private Aspect[] aspectTriggers; + + private ResearchPage[] pages = null; + + public ResearchItem(String key, String category) + { + this.key = key; + this.category = category; + this.tags = new AspectList(); + this.icon_resource = null; + this.icon_item = null; + this.displayColumn = 0; + this.displayRow = 0; + this.setVirtual(); + + } + + public ResearchItem(String key, String category, AspectList tags, int col, int row, int complex, ResourceLocation icon) + { + this.key = key; + this.category = category; + this.tags = tags; + this.icon_resource = icon; + this.icon_item = null; + this.displayColumn = col; + this.displayRow = row; + this.complexity = complex; + if (complexity < 1) this.complexity = 1; + if (complexity > 3) this.complexity = 3; + } + + public ResearchItem(String key, String category, AspectList tags, int col, int row, int complex, ItemStack icon) + { + this.key = key; + this.category = category; + this.tags = tags; + this.icon_item = icon; + this.icon_resource = null; + this.displayColumn = col; + this.displayRow = row; + this.complexity = complex; + if (complexity < 1) this.complexity = 1; + if (complexity > 3) this.complexity = 3; + } + + public ResearchItem setSpecial() + { + this.isSpecial = true; + return this; + } + + public ResearchItem setStub() + { + this.isStub = true; + return this; + } + + @Deprecated + public ResearchItem setLost() + { + this.isLost = true; + return this; + } + + public ResearchItem setConcealed() + { + this.isConcealed = true; + return this; + } + + public ResearchItem setHidden() + { + this.isHidden = true; + return this; + } + + public ResearchItem setVirtual() + { + this.isVirtual = true; + return this; + } + + public ResearchItem setParents(String... par) + { + this.parents = par; + return this; + } + + + + public ResearchItem setParentsHidden(String... par) + { + this.parentsHidden = par; + return this; + } + + public ResearchItem setSiblings(String... sib) + { + this.siblings = sib; + return this; + } + + public ResearchItem setPages(ResearchPage... par) + { + this.pages = par; + return this; + } + + public ResearchPage[] getPages() { + return pages; + } + + public ResearchItem setItemTriggers(ItemStack... par) + { + this.itemTriggers = par; + return this; + } + + public ResearchItem setEntityTriggers(String... par) + { + this.entityTriggers = par; + return this; + } + + public ResearchItem setAspectTriggers(Aspect... par) + { + this.aspectTriggers = par; + return this; + } + + public ItemStack[] getItemTriggers() { + return itemTriggers; + } + + public String[] getEntityTriggers() { + return entityTriggers; + } + + public Aspect[] getAspectTriggers() { + return aspectTriggers; + } + + public ResearchItem registerResearchItem() + { + ResearchCategories.addResearch(this); + return this; + } + + public String getName() + { + return StatCollector.translateToLocal("tc.research_name."+key); + } + + public String getText() + { + return StatCollector.translateToLocal("tc.research_text."+key); + } + + public boolean isSpecial() + { + return this.isSpecial; + } + + public boolean isStub() + { + return this.isStub; + } + + @Deprecated + public boolean isLost() + { + return this.isLost; + } + + public boolean isConcealed() + { + return this.isConcealed; + } + + public boolean isHidden() + { + return this.isHidden; + } + + public boolean isVirtual() + { + return this.isVirtual; + } + + public boolean isAutoUnlock() { + return isAutoUnlock; + } + + public ResearchItem setAutoUnlock() + { + this.isAutoUnlock = true; + return this; + } + + public boolean isRound() { + return isRound; + } + + public ResearchItem setRound() { + this.isRound = true; + return this; + } + + public boolean isSecondary() { + return isSecondary; + } + + public ResearchItem setSecondary() { + this.isSecondary = true; + return this; + } + + public int getComplexity() { + return complexity; + } + + public ResearchItem setComplexity(int complexity) { + this.complexity = complexity; + return this; + } + + /** + * @return the aspect aspects ordinal with the highest value. Used to determine scroll color and similar things + */ + public Aspect getResearchPrimaryTag() { + Aspect aspect=null; + int highest=0; + if (tags!=null) + for (Aspect tag:tags.getAspects()) { + if (tags.getAmount(tag)>highest) { + aspect=tag; + highest=tags.getAmount(tag); + }; + } + return aspect; + } + +} diff --git a/src/api/java/thaumcraft/api/research/ResearchPage.java b/src/api/java/thaumcraft/api/research/ResearchPage.java new file mode 100644 index 0000000..fdada84 --- /dev/null +++ b/src/api/java/thaumcraft/api/research/ResearchPage.java @@ -0,0 +1,193 @@ +package thaumcraft.api.research; + +import java.util.List; + +import net.minecraft.item.ItemStack; +import net.minecraft.item.crafting.FurnaceRecipes; +import net.minecraft.item.crafting.IRecipe; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; +import thaumcraft.api.aspects.AspectList; +import thaumcraft.api.crafting.CrucibleRecipe; +import thaumcraft.api.crafting.IArcaneRecipe; +import thaumcraft.api.crafting.InfusionEnchantmentRecipe; +import thaumcraft.api.crafting.InfusionRecipe; + +public class ResearchPage { + public static enum PageType + { + TEXT, + TEXT_CONCEALED, + IMAGE, + CRUCIBLE_CRAFTING, + ARCANE_CRAFTING, + ASPECTS, + NORMAL_CRAFTING, + INFUSION_CRAFTING, + COMPOUND_CRAFTING, + INFUSION_ENCHANTMENT, + SMELTING + } + + public PageType type = PageType.TEXT; + + public String text=null; + public String research=null; + public ResourceLocation image=null; + public AspectList aspects=null; + public Object recipe=null; + public ItemStack recipeOutput=null; + + /** + * @param text this can (but does not have to) be a reference to a localization variable, not the actual text. + */ + public ResearchPage(String text) { + this.type = PageType.TEXT; + this.text = text; + } + + /** + * @param research this page will only be displayed if the player has discovered this research + * @param text this can (but does not have to) be a reference to a localization variable, not the actual text. + */ + public ResearchPage(String research, String text) { + this.type = PageType.TEXT_CONCEALED; + this.research = research; + this.text = text; + } + + /** + * @param recipe a vanilla crafting recipe. + */ + public ResearchPage(IRecipe recipe) { + this.type = PageType.NORMAL_CRAFTING; + this.recipe = recipe; + this.recipeOutput = recipe.getRecipeOutput(); + } + + /** + * @param recipe a collection of vanilla crafting recipes. + */ + public ResearchPage(IRecipe[] recipe) { + this.type = PageType.NORMAL_CRAFTING; + this.recipe = recipe; + } + + /** + * @param recipe a collection of arcane crafting recipes. + */ + public ResearchPage(IArcaneRecipe[] recipe) { + this.type = PageType.ARCANE_CRAFTING; + this.recipe = recipe; + } + + /** + * @param recipe a collection of arcane crafting recipes. + */ + public ResearchPage(CrucibleRecipe[] recipe) { + this.type = PageType.CRUCIBLE_CRAFTING; + this.recipe = recipe; + } + + /** + * @param recipe a collection of infusion crafting recipes. + */ + public ResearchPage(InfusionRecipe[] recipe) { + this.type = PageType.INFUSION_CRAFTING; + this.recipe = recipe; + } + + /** + * @param recipe a compound crafting recipe. + */ + public ResearchPage(List recipe) { + this.type = PageType.COMPOUND_CRAFTING; + this.recipe = recipe; + } + + /** + * @param recipe an arcane worktable crafting recipe. + */ + public ResearchPage(IArcaneRecipe recipe) { + this.type = PageType.ARCANE_CRAFTING; + this.recipe = recipe; + this.recipeOutput = recipe.getRecipeOutput(); + } + + /** + * @param recipe an alchemy crafting recipe. + */ + public ResearchPage(CrucibleRecipe recipe) { + this.type = PageType.CRUCIBLE_CRAFTING; + this.recipe = recipe; + this.recipeOutput = recipe.getRecipeOutput(); + } + + /** + * @param recipe a furnace smelting crafting recipe. + */ + public ResearchPage(ItemStack input) { + this.type = PageType.SMELTING; + this.recipe = input; + this.recipeOutput = FurnaceRecipes.smelting().getSmeltingResult(input); + } + + /** + * @param recipe an infusion crafting recipe. + */ + public ResearchPage(InfusionRecipe recipe) { + this.type = PageType.INFUSION_CRAFTING; + this.recipe = recipe; + if (recipe.getRecipeOutput() instanceof ItemStack) { + this.recipeOutput = (ItemStack) recipe.getRecipeOutput(); + } else { + this.recipeOutput = recipe.getRecipeInput(); + } + } + + /** + * @param recipe an infusion crafting recipe. + */ + public ResearchPage(InfusionEnchantmentRecipe recipe) { + this.type = PageType.INFUSION_ENCHANTMENT; + this.recipe = recipe; +// if (recipe.recipeOutput instanceof ItemStack) { +// this.recipeOutput = (ItemStack) recipe.recipeOutput; +// } else { +// this.recipeOutput = recipe.recipeInput; +// } + } + + /** + * @param image + * @param caption this can (but does not have to) be a reference to a localization variable, not the actual text. + */ + public ResearchPage(ResourceLocation image, String caption) { + this.type = PageType.IMAGE; + this.image = image; + this.text = caption; + } + + /** + * This function should really not be called directly - used internally + */ + public ResearchPage(AspectList as) { + this.type = PageType.ASPECTS; + this.aspects = as; + } + + /** + * returns a localized text of the text field (if one exists). Returns the text field itself otherwise. + * @return + */ + public String getTranslatedText() { + String ret=""; + if (text != null) { + ret = StatCollector.translateToLocal(text); + if (ret.isEmpty()) ret = text; + } + return ret; + } + + +} diff --git a/src/api/java/thaumcraft/api/research/ScanResult.java b/src/api/java/thaumcraft/api/research/ScanResult.java new file mode 100644 index 0000000..e1498f3 --- /dev/null +++ b/src/api/java/thaumcraft/api/research/ScanResult.java @@ -0,0 +1,39 @@ +package thaumcraft.api.research; + +import net.minecraft.entity.Entity; + +public class ScanResult { + public byte type = 0; //1=blocks,2=entities,3=phenomena + public int id; + public int meta; + public Entity entity; + public String phenomena; + + public ScanResult(byte type, int blockId, int blockMeta, Entity entity, + String phenomena) { + super(); + this.type = type; + this.id = blockId; + this.meta = blockMeta; + this.entity = entity; + this.phenomena = phenomena; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof ScanResult) { + ScanResult sr = (ScanResult) obj; + if (type != sr.type) + return false; + if (type == 1 + && (id != sr.id || meta != sr.meta)) + return false; + if (type == 2 && entity.getEntityId() != sr.entity.getEntityId()) + return false; + if (type == 3 && !phenomena.equals(sr.phenomena)) + return false; + } + return true; + } + +} diff --git a/src/api/java/thaumcraft/api/visnet/TileVisNode.java b/src/api/java/thaumcraft/api/visnet/TileVisNode.java new file mode 100644 index 0000000..84b1400 --- /dev/null +++ b/src/api/java/thaumcraft/api/visnet/TileVisNode.java @@ -0,0 +1,188 @@ +package thaumcraft.api.visnet; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; + +import thaumcraft.api.TileThaumcraft; +import thaumcraft.api.WorldCoordinates; +import thaumcraft.api.aspects.Aspect; + +/** + * @author Azanor + * + * The tile entity used by nodes in the vis energy network. A node is either a source (like an aura node), + * a transport relay or vis receiver (like the infernal furnace). + * + */ +public abstract class TileVisNode extends TileThaumcraft { + + WeakReference parent = null; + ArrayList> children = new ArrayList>(); + + /** + * @return the WorldCoordinates location of where this node is located + */ + public WorldCoordinates getLocation() { + return new WorldCoordinates(this); + } + + /** + * @return the number of blocks away this node will check for parent nodes to connect to. + */ + public abstract int getRange(); + + /** + * @return true if this is the source or root node of the vis network. + */ + public abstract boolean isSource(); + + /** + * This method should never be called directly. Use VisNetHandler.drainVis() instead + * @param aspect what aspect to drain + * @param vis how much to drain + * @return how much was actually drained + */ + public int consumeVis(Aspect aspect, int vis) { + if (VisNetHandler.isNodeValid(getParent())) { + int out = getParent().get().consumeVis(aspect, vis); + if (out>0) { + triggerConsumeEffect(aspect); + } + return out; + } + return 0; + } + + public void removeThisNode() { + for (WeakReference n:getChildren()) { + if (n!=null && n.get()!=null) { + n.get().removeThisNode(); + } + } + + children = new ArrayList>(); + if (VisNetHandler.isNodeValid(this.getParent())) { + this.getParent().get().nodeRefresh=true; + } + this.setParent(null); + this.parentChanged(); + + if (this.isSource()) { + HashMap> sourcelist = VisNetHandler.sources.get(worldObj.provider.dimensionId); + if (sourcelist==null) { + sourcelist = new HashMap>(); + } + sourcelist.remove(getLocation()); + VisNetHandler.sources.put( worldObj.provider.dimensionId, sourcelist ); + } + + worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); + } + + + + @Override + public void invalidate() { + removeThisNode(); + super.invalidate(); + } + + public void triggerConsumeEffect(Aspect aspect) { } + + /** + * @return + */ + public WeakReference getParent() { + return parent; + } + + /** + * @return + */ + public WeakReference getRootSource() { + return VisNetHandler.isNodeValid(getParent()) ? + getParent().get().getRootSource() : this.isSource() ? + new WeakReference(this) : null; + } + + /** + * @param parent + */ + public void setParent(WeakReference parent) { + this.parent = parent; + } + + /** + * @return + */ + public ArrayList> getChildren() { + return children; + } + + @Override + public boolean canUpdate() { + return true; + } + + protected int nodeCounter = 0; + private boolean nodeRegged = false; + public boolean nodeRefresh = false; + + @Override + public void updateEntity() { + + if (!worldObj.isRemote && ((nodeCounter++) % 40==0 || nodeRefresh)) { + //check for changes + if (!nodeRefresh && children.size()>0) { + for (WeakReference n:children) { + if (n==null || n.get()==null) { + nodeRefresh=true; + break; + } + } + } + + //refresh linked nodes + if (nodeRefresh) { + for (WeakReference n:children) { + if (n.get()!=null) { + n.get().nodeRefresh=true; + } + } + children.clear(); + parent=null; + } + + //redo stuff + if (isSource() && !nodeRegged) { + VisNetHandler.addSource(getWorldObj(), this); + nodeRegged = true; + } else + if (!isSource() && !VisNetHandler.isNodeValid(getParent())) { + setParent(VisNetHandler.addNode(getWorldObj(), this)); + nodeRefresh=true; + } + + if (nodeRefresh) { + worldObj.markBlockForUpdate(xCoord, yCoord, zCoord); + parentChanged(); + } + nodeRefresh=false; + } + + } + + public void parentChanged() { } + + /** + * @return the type of shard this is attuned to: + * none -1, air 0, fire 1, water 2, earth 3, order 4, entropy 5 + * Should return -1 for most implementations + */ + public byte getAttunement() { + return -1; + } + + +} diff --git a/src/api/java/thaumcraft/api/visnet/VisNetHandler.java b/src/api/java/thaumcraft/api/visnet/VisNetHandler.java new file mode 100644 index 0000000..cb9714c --- /dev/null +++ b/src/api/java/thaumcraft/api/visnet/VisNetHandler.java @@ -0,0 +1,284 @@ +package thaumcraft.api.visnet; + +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; + +import net.minecraft.world.World; +import thaumcraft.api.WorldCoordinates; +import thaumcraft.api.aspects.Aspect; +import cpw.mods.fml.common.FMLLog; + +public class VisNetHandler { + + // / NODE DRAINING + /** + * This method drains vis from a relay or source near the passed in + * location. The amount received can be less than the amount requested so + * take that into account. + * + * @param world + * @param x the x position of the draining block or entity + * @param y the y position of the draining block or entity + * @param z the z position of the draining block or entity + * @param aspect what aspect to drain + * @param amount how much to drain + * @return how much was actually drained + */ + public static int drainVis(World world, int x, int y, int z, Aspect aspect, int amount) { + + int drainedAmount = 0; + + WorldCoordinates drainer = new WorldCoordinates(x, y, z, + world.provider.dimensionId); + if (!nearbyNodes.containsKey(drainer)) { + calculateNearbyNodes(world, x, y, z); + } + + ArrayList> nodes = nearbyNodes.get(drainer); + if (nodes!=null && nodes.size()>0) + for (WeakReference noderef : nodes) { + + TileVisNode node = noderef.get(); + + if (node == null) continue; + + int a = node.consumeVis(aspect, amount); + drainedAmount += a; + amount -= a; + if (a>0) { + int color = Aspect.getPrimalAspects().indexOf(aspect); + generateVisEffect(world.provider.dimensionId, x, y, z, node.xCoord, node.yCoord, node.zCoord, color); + } + if (amount <= 0) { + break; + } + } + + return drainedAmount; + } + + static Method generateVisEffect; + public static void generateVisEffect(int dim, int x, int y, int z, int x2, int y2, int z2, int color) { + try { + if(generateVisEffect == null) { + Class fake = Class.forName("thaumcraft.common.lib.Utils"); + generateVisEffect = fake.getMethod("generateVisEffect", int.class, int.class, int.class, int.class, int.class, int.class, int.class, int.class); + } + generateVisEffect.invoke(null, dim, x,y,z,x2,y2,z2,color); + } catch(Exception ex) { + FMLLog.warning("[Thaumcraft API] Could not invoke thaumcraft.common.lib.Utils method generateVisEffect"); + } + } + + public static HashMap>> sources = new HashMap>>(); + + public static void addSource(World world, TileVisNode vs) { + HashMap> sourcelist = sources + .get(world.provider.dimensionId); + if (sourcelist == null) { + sourcelist = new HashMap>(); + } + sourcelist.put(vs.getLocation(), new WeakReference(vs)); + sources.put(world.provider.dimensionId, sourcelist); + nearbyNodes.clear(); + } + + public static boolean isNodeValid(WeakReference node) { + if (node == null || node.get() == null || node.get().isInvalid()) + return false; + return true; + } + + public static WeakReference addNode(World world, TileVisNode vn) { + WeakReference ref = new WeakReference(vn); + + HashMap> sourcelist = sources + .get(world.provider.dimensionId); + if (sourcelist == null) { + sourcelist = new HashMap>(); + return null; + } + + ArrayList nearby = new ArrayList(); + + for (WeakReference root : sourcelist.values()) { + if (!isNodeValid(root)) + continue; + + TileVisNode source = root.get(); + + float r = inRange(world, vn.getLocation(), source.getLocation(), + vn.getRange()); + if (r > 0) { + nearby.add(new Object[] { source, r - vn.getRange() * 2 }); + } + nearby = findClosestNodes(vn, source, nearby); + } + + float dist = Float.MAX_VALUE; + TileVisNode closest = null; + if (nearby.size() > 0) { + for (Object[] o : nearby) { + if ((Float) o[1] < dist) {// && canNodeBeSeen(vn,(TileVisNode) + // o[0])) { + dist = (Float) o[1]; + closest = (TileVisNode) o[0]; + } + } + } + if (closest != null) { + closest.getChildren().add(ref); + nearbyNodes.clear(); + return new WeakReference(closest); + } + + return null; + } + + public static ArrayList findClosestNodes(TileVisNode target, + TileVisNode root, ArrayList in) { + TileVisNode closestChild = null; + + for (WeakReference child : root.getChildren()) { + TileVisNode n = child.get(); + + if (n != null + && !n.equals(target) + && !n.equals(root) + && (target.getAttunement() == -1 || n.getAttunement() == -1 || n + .getAttunement() == target.getAttunement())) { + + float r2 = inRange(n.getWorldObj(), n.getLocation(), + target.getLocation(), target.getRange()); + if (r2 > 0) { + in.add(new Object[] { n, r2 }); + } + + in = findClosestNodes(target, n, in); + } + } + return in; + } + + private static float inRange(World world, WorldCoordinates cc1, + WorldCoordinates cc2, int range) { + float distance = cc1.getDistanceSquaredToWorldCoordinates(cc2); + return distance > range * range ? -1 : distance; + } + + private static HashMap>> nearbyNodes = new HashMap>>(); + + private static void calculateNearbyNodes(World world, int x, int y, int z) { + + HashMap> sourcelist = sources + .get(world.provider.dimensionId); + if (sourcelist == null) { + sourcelist = new HashMap>(); + return; + } + + ArrayList> cn = new ArrayList>(); + WorldCoordinates drainer = new WorldCoordinates(x, y, z, + world.provider.dimensionId); + + ArrayList nearby = new ArrayList(); + + for (WeakReference root : sourcelist.values()) { + + if (!isNodeValid(root)) + continue; + + TileVisNode source = root.get(); + + TileVisNode closest = null; + float range = Float.MAX_VALUE; + + float r = inRange(world, drainer, source.getLocation(), + source.getRange()); + if (r > 0) { + range = r; + closest = source; + } + + ArrayList> children = new ArrayList>(); + children = getAllChildren(source,children); + + for (WeakReference child : children) { + TileVisNode n = child.get(); + if (n != null && !n.equals(root)) { + + float r2 = inRange(n.getWorldObj(), n.getLocation(), + drainer, n.getRange()); + if (r2 > 0 && r2 < range) { + range = r2; + closest = n; + } + } + } + + if (closest != null) { + + cn.add(new WeakReference(closest)); + } + } + + nearbyNodes.put(drainer, cn); + } + + private static ArrayList> getAllChildren(TileVisNode source, ArrayList> list) { + for (WeakReference child : source.getChildren()) { + TileVisNode n = child.get(); + if (n != null) { + list.add(child); + list = getAllChildren(n,list); + } + } + return list; + } + + // public static boolean canNodeBeSeen(TileVisNode source,TileVisNode + // target) + // { + // double d = Math.sqrt(source.getDistanceFrom(target.xCoord, target.yCoord, + // target.zCoord)); + // double xd = (source.xCoord-target.xCoord) / d; + // double yd = (source.yCoord-target.yCoord) / d; + // double zd = (source.zCoord-target.zCoord) / d; + // return source.getWorldObj().rayTraceBlocks( + // Vec3.createVectorHelper(source.xCoord-xd+.5+.5, source.yCoord-yd, + // source.zCoord-zd), + // Vec3.createVectorHelper(target.xCoord+.5, target.yCoord+.5, + // target.zCoord+.5)) == null; + // } + + // public static HashMap> + // noderef = new HashMap>(); + // + // public static TileVisNode getClosestNodeWithinRadius(World world, int x, + // int y, int z, int radius) { + // TileVisNode out = null; + // WorldCoordinates wc = null; + // float cd = Float.MAX_VALUE; + // for (int sx = x - radius; sx <= x + radius; sx++) { + // for (int sy = y - radius; sy <= y + radius; sy++) { + // for (int sz = z - radius; sz <= z + radius; sz++) { + // wc = new WorldCoordinates(sx,sy,sz,world.provider.dimensionId); + // if (noderef.containsKey(wc)) { + // float d = wc.getDistanceSquared(x, y, z); + // if (dAs a guide build the sort string from two alphanumeric characters followed by + * two numeric characters based on... whatever. + */ + public String getSortingHelper(ItemStack itemstack); + + boolean onFocusBlockStartBreak(ItemStack itemstack, int x, int y, int z, EntityPlayer player); + + public boolean acceptsEnchant(int id); + + + + + +} diff --git a/src/api/java/thaumcraft/api/wands/IWandRodOnUpdate.java b/src/api/java/thaumcraft/api/wands/IWandRodOnUpdate.java new file mode 100644 index 0000000..4ef8c84 --- /dev/null +++ b/src/api/java/thaumcraft/api/wands/IWandRodOnUpdate.java @@ -0,0 +1,16 @@ +package thaumcraft.api.wands; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; + +/** + * + * @author azanor + * + * Implemented by a class that you wish to be called whenever a wand with this rod performs its + * update tick. + * + */ +public interface IWandRodOnUpdate { + void onUpdate(ItemStack itemstack, EntityPlayer player); +} diff --git a/src/api/java/thaumcraft/api/wands/IWandTriggerManager.java b/src/api/java/thaumcraft/api/wands/IWandTriggerManager.java new file mode 100644 index 0000000..7c299de --- /dev/null +++ b/src/api/java/thaumcraft/api/wands/IWandTriggerManager.java @@ -0,0 +1,15 @@ +package thaumcraft.api.wands; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +public interface IWandTriggerManager { + + /** + * This class will be called by wands with the proper parameters. It is up to you to decide what to do with them. + */ + public boolean performTrigger(World world, ItemStack wand, EntityPlayer player, + int x, int y, int z, int side, int event); + +} diff --git a/src/api/java/thaumcraft/api/wands/IWandable.java b/src/api/java/thaumcraft/api/wands/IWandable.java new file mode 100644 index 0000000..aeb9bac --- /dev/null +++ b/src/api/java/thaumcraft/api/wands/IWandable.java @@ -0,0 +1,25 @@ +package thaumcraft.api.wands; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +/** + * + * @author azanor + * + * Add this to a tile entity that you wish wands to interact with in some way. + * + */ + +public interface IWandable { + + public int onWandRightClick(World world, ItemStack wandstack, EntityPlayer player, int x, int y, int z, int side, int md); + + public ItemStack onWandRightClick(World world, ItemStack wandstack, EntityPlayer player); + + public void onUsingWandTick(ItemStack wandstack, EntityPlayer player, int count); + + public void onWandStoppedUsing(ItemStack wandstack, World world, EntityPlayer player, int count); + +} diff --git a/src/api/java/thaumcraft/api/wands/ItemFocusBasic.java b/src/api/java/thaumcraft/api/wands/ItemFocusBasic.java new file mode 100644 index 0000000..3590052 --- /dev/null +++ b/src/api/java/thaumcraft/api/wands/ItemFocusBasic.java @@ -0,0 +1,166 @@ +package thaumcraft.api.wands; + +import java.text.DecimalFormat; +import java.util.List; +import java.util.Map; + +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.EnumRarity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.IIcon; +import net.minecraft.util.MovingObjectPosition; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import thaumcraft.api.ThaumcraftApi; +import thaumcraft.api.aspects.Aspect; +import thaumcraft.api.aspects.AspectList; +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; + +public class ItemFocusBasic extends Item implements IWandFocus { + + public ItemFocusBasic () + { + super(); + maxStackSize = 1; + canRepair=false; + this.setMaxDamage(0); + } + + public IIcon icon; + + @SideOnly(Side.CLIENT) + @Override + public IIcon getIconFromDamage(int par1) { + return icon; + } + + @Override + public boolean isItemTool(ItemStack par1ItemStack) + { + return true; + } + + @Override + public boolean isDamageable() { + return true; + } + + @Override + public void addInformation(ItemStack stack,EntityPlayer player, List list, boolean par4) { + AspectList al = this.getVisCost(); + if (al!=null && al.size()>0) { + list.add(StatCollector.translateToLocal(isVisCostPerTick()?"item.Focus.cost2":"item.Focus.cost1")); + for (Aspect aspect:al.getAspectsSorted()) { + DecimalFormat myFormatter = new DecimalFormat("#####.##"); + String amount = myFormatter.format(al.getAmount(aspect)/100f); + list.add(" \u00A7"+aspect.getChatcolor()+aspect.getName()+"\u00A7r x "+ amount); + + } + } + } + + @Override + public int getItemEnchantability() { + return 5; + } + + @Override + public EnumRarity getRarity(ItemStack itemstack) + { + return EnumRarity.rare; + } + + + @Override + public int getFocusColor() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public AspectList getVisCost() { + // TODO Auto-generated method stub + return null; + } + + @Override + public ItemStack onFocusRightClick(ItemStack itemstack, World world, + EntityPlayer player, MovingObjectPosition movingobjectposition) { + // TODO Auto-generated method stub + return null; + } + + @Override + public void onUsingFocusTick(ItemStack itemstack, EntityPlayer player, + int count) { + // TODO Auto-generated method stub + } + + @Override + public void onPlayerStoppedUsingFocus(ItemStack itemstack, World world, + EntityPlayer player, int count) { + // TODO Auto-generated method stub + + } + + /** + * Just insert two alphanumeric characters before this string in your focus item class + */ + @Override + public String getSortingHelper(ItemStack itemstack) { + Map ench = EnchantmentHelper.getEnchantments(itemstack); + String out=""; + for (Integer lvl:ench.values()) { + out = out + lvl + ""; + } + return out; + } + + @Override + public boolean isVisCostPerTick() { + return false; + } + + @Override + public IIcon getOrnament() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean onFocusBlockStartBreak(ItemStack itemstack, int x, int y, + int z, EntityPlayer player) { + // TODO Auto-generated method stub + return false; + } + + @Override + public WandFocusAnimation getAnimation() { + return WandFocusAnimation.WAVE; + } + + @Override + public IIcon getFocusDepthLayerIcon() { + // TODO Auto-generated method stub + return null; + } + + /** + * @see thaumcraft.api.wands.IWandFocus#acceptsEnchant(int) + * By default fortune is off for all wands + **/ + @Override + public boolean acceptsEnchant(int id) { + if (id==ThaumcraftApi.enchantFrugal|| + id==ThaumcraftApi.enchantPotency) return true; + return false; + } + + + + + +} diff --git a/src/api/java/thaumcraft/api/wands/StaffRod.java b/src/api/java/thaumcraft/api/wands/StaffRod.java new file mode 100644 index 0000000..e7ae90f --- /dev/null +++ b/src/api/java/thaumcraft/api/wands/StaffRod.java @@ -0,0 +1,48 @@ +package thaumcraft.api.wands; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; + +/** + * + * @author Azanor + * + * This class is used to keep the material information for the various rods. + * It is also used to generate the wand recipes ingame. + * + */ +public class StaffRod extends WandRod { + + boolean runes=false; + + public StaffRod(String tag, int capacity, ItemStack item, int craftCost) { + super(tag+"_staff", capacity, item, craftCost); + this.texture = new ResourceLocation("thaumcraft","textures/models/wand_rod_"+tag+".png"); + } + + public StaffRod(String tag, int capacity, ItemStack item, int craftCost, + IWandRodOnUpdate onUpdate, ResourceLocation texture) { + super(tag+"_staff", capacity, item, craftCost, onUpdate, texture); + } + + public StaffRod(String tag, int capacity, ItemStack item, int craftCost, + IWandRodOnUpdate onUpdate) { + super(tag+"_staff", capacity, item, craftCost, onUpdate); + this.texture = new ResourceLocation("thaumcraft","textures/models/wand_rod_"+tag+".png"); + } + + public StaffRod(String tag, int capacity, ItemStack item, int craftCost, + ResourceLocation texture) { + super(tag+"_staff", capacity, item, craftCost, texture); + } + + public boolean hasRunes() { + return runes; + } + + public void setRunes(boolean hasRunes) { + this.runes = hasRunes; + } + + +} diff --git a/src/api/java/thaumcraft/api/wands/WandCap.java b/src/api/java/thaumcraft/api/wands/WandCap.java new file mode 100644 index 0000000..17b4581 --- /dev/null +++ b/src/api/java/thaumcraft/api/wands/WandCap.java @@ -0,0 +1,129 @@ +package thaumcraft.api.wands; + +import java.util.LinkedHashMap; +import java.util.List; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; +import thaumcraft.api.aspects.Aspect; + +/** + * This class is used to keep the material information for the various caps. + * It is also used to generate the wand recipes ingame. + * @author Azanor + * + */ +public class WandCap { + + private String tag; + + /** + * Cost to craft this wand. Combined with the rod cost. + */ + private int craftCost; + + /** + * the amount by which all aspect costs are multiplied + */ + float baseCostModifier; + + /** + * specifies a list of primal aspects that use the special discount figure instead of the normal discount. + */ + List specialCostModifierAspects; + + /** + * the amount by which the specified aspect costs are multiplied + */ + float specialCostModifier; + + /** + * The texture that will be used for the ingame wand cap + */ + ResourceLocation texture; + + /** + * the actual item that makes up this cap and will be used to generate the wand recipes + */ + ItemStack item; + + public static LinkedHashMap caps = new LinkedHashMap(); + + public WandCap (String tag, float discount, ItemStack item, int craftCost) { + this.setTag(tag); + this.baseCostModifier = discount; + this.specialCostModifierAspects = null; + texture = new ResourceLocation("thaumcraft","textures/models/wand_cap_"+getTag()+".png"); + this.item=item; + this.setCraftCost(craftCost); + caps.put(tag, this); + } + + public WandCap (String tag, float discount, List specialAspects, float discountSpecial, ItemStack item, int craftCost) { + this.setTag(tag); + this.baseCostModifier = discount; + this.specialCostModifierAspects = specialAspects; + this.specialCostModifier = discountSpecial; + texture = new ResourceLocation("thaumcraft","textures/models/wand_cap_"+getTag()+".png"); + this.item=item; + this.setCraftCost(craftCost); + caps.put(tag, this); + } + + public float getBaseCostModifier() { + return baseCostModifier; + } + + public List getSpecialCostModifierAspects() { + return specialCostModifierAspects; + } + + public float getSpecialCostModifier() { + return specialCostModifier; + } + + public ResourceLocation getTexture() { + return texture; + } + + public void setTexture(ResourceLocation texture) { + this.texture = texture; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + + public ItemStack getItem() { + return item; + } + + public void setItem(ItemStack item) { + this.item = item; + } + + public int getCraftCost() { + return craftCost; + } + + public void setCraftCost(int craftCost) { + this.craftCost = craftCost; + } + + /** + * The research a player needs to have finished to be able to craft a wand with this cap. + */ + public String getResearch() { + return "CAP_"+getTag(); + } + + // Some examples: + // WandCap WAND_CAP_IRON = new WandCap("iron", 1.1f, Arrays.asList(Aspect.ORDER),1, new ItemStack(ConfigItems.itemWandCap,1,0),1); + // WandCap WAND_CAP_GOLD = new WandCap("gold", 1f, new ItemStack(ConfigItems.itemWandCap,1,1),3); + +} diff --git a/src/api/java/thaumcraft/api/wands/WandRod.java b/src/api/java/thaumcraft/api/wands/WandRod.java new file mode 100644 index 0000000..85954e4 --- /dev/null +++ b/src/api/java/thaumcraft/api/wands/WandRod.java @@ -0,0 +1,158 @@ +package thaumcraft.api.wands; + +import java.util.LinkedHashMap; + +import net.minecraft.item.ItemStack; +import net.minecraft.util.ResourceLocation; + +/** + * + * @author Azanor + * + * This class is used to keep the material information for the various rods. + * It is also used to generate the wand recipes ingame. + * + */ +public class WandRod { + + + private String tag; + + /** + * Cost to craft this wand. Combined with the rod cost. + */ + private int craftCost; + + /** + * The amount of vis that can be stored - this number is actually multiplied + * by 100 for use by the wands internals + */ + int capacity; + + /** + * The texture that will be used for the ingame wand rod + */ + protected ResourceLocation texture; + + /** + * the actual item that makes up this rod and will be used to generate the wand recipes + */ + ItemStack item; + + /** + * A class that will be called whenever the wand onUpdate tick is run + */ + IWandRodOnUpdate onUpdate; + + /** + * Does the rod glow in the dark? + */ + boolean glow; + + public static LinkedHashMap rods = new LinkedHashMap(); + + public WandRod (String tag, int capacity, ItemStack item, int craftCost, ResourceLocation texture) { + this.setTag(tag); + this.capacity = capacity; + this.texture = texture; + this.item=item; + this.setCraftCost(craftCost); + rods.put(tag, this); + } + + public WandRod (String tag, int capacity, ItemStack item, int craftCost, IWandRodOnUpdate onUpdate, ResourceLocation texture) { + this.setTag(tag); + this.capacity = capacity; + this.texture = texture; + this.item=item; + this.setCraftCost(craftCost); + rods.put(tag, this); + this.onUpdate = onUpdate; + } + + public WandRod (String tag, int capacity, ItemStack item, int craftCost) { + this.setTag(tag); + this.capacity = capacity; + this.texture = new ResourceLocation("thaumcraft","textures/models/wand_rod_"+getTag()+".png"); + this.item=item; + this.setCraftCost(craftCost); + rods.put(tag, this); + } + + public WandRod (String tag, int capacity, ItemStack item, int craftCost, IWandRodOnUpdate onUpdate) { + this.setTag(tag); + this.capacity = capacity; + this.texture = new ResourceLocation("thaumcraft","textures/models/wand_rod_"+getTag()+".png"); + this.item=item; + this.setCraftCost(craftCost); + rods.put(tag, this); + this.onUpdate = onUpdate; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public int getCapacity() { + return capacity; + } + + public void setCapacity(int capacity) { + this.capacity = capacity; + } + + public ResourceLocation getTexture() { + return texture; + } + + public void setTexture(ResourceLocation texture) { + this.texture = texture; + } + + public ItemStack getItem() { + return item; + } + + public void setItem(ItemStack item) { + this.item = item; + } + + public int getCraftCost() { + return craftCost; + } + + public void setCraftCost(int craftCost) { + this.craftCost = craftCost; + } + + public IWandRodOnUpdate getOnUpdate() { + return onUpdate; + } + + public void setOnUpdate(IWandRodOnUpdate onUpdate) { + this.onUpdate = onUpdate; + } + + public boolean isGlowing() { + return glow; + } + + public void setGlowing(boolean hasGlow) { + this.glow = hasGlow; + } + + /** + * The research a player needs to have finished to be able to craft a wand with this rod. + */ + public String getResearch() { + return "ROD_"+getTag(); + } + + // Some examples: + // WandRod WAND_ROD_WOOD = new WandRod("wood",25,new ItemStack(Item.stick),1); + // WandRod WAND_ROD_BLAZE = new WandRod("blaze",100,new ItemStack(Item.blazeRod),7,new WandRodBlazeOnUpdate()); +} diff --git a/src/api/java/thaumcraft/api/wands/WandTriggerRegistry.java b/src/api/java/thaumcraft/api/wands/WandTriggerRegistry.java new file mode 100644 index 0000000..7224e12 --- /dev/null +++ b/src/api/java/thaumcraft/api/wands/WandTriggerRegistry.java @@ -0,0 +1,126 @@ +package thaumcraft.api.wands; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +import net.minecraft.block.Block; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.world.World; + +/** + * This class serves a similar function to IWandable in that it allows wands to interact + * with object in the world. In this case it is most useful for adding interaction with non-mod + * blocks where you can't control what happens in their code. + * Example where it is used is in crafting the thaumonomicon from a bookshelf and the + * crucible from a cauldron + * + * @author azanor + * + */ +public class WandTriggerRegistry { + + private static HashMap> triggers = new HashMap>(); + private static final String DEFAULT = "default"; + + /** + * Registers an action to perform when a casting wand right clicks on a specific block. + * A manager class needs to be created that implements IWandTriggerManager. + * @param manager + * @param event a logical number that you can use to differentiate different events or actions + * @param block + * @param meta send -1 as a wildcard value for all possible meta values + * @param modid a unique identifier. It is best to register your own triggers using your mod id to avoid conflicts with mods that register triggers for the same block + */ + public static void registerWandBlockTrigger(IWandTriggerManager manager, int event, Block block, int meta, String modid) { + if (!triggers.containsKey(modid)) { + triggers.put(modid, new HashMap()); + } + HashMap temp = triggers.get(modid); + temp.put(Arrays.asList(block,meta),Arrays.asList(manager,event)); + triggers.put(modid, temp); + } + + /** + * for legacy support + */ + public static void registerWandBlockTrigger(IWandTriggerManager manager, int event, Block block, int meta) { + registerWandBlockTrigger(manager, event, block, meta, DEFAULT); + } + + /** + * Checks all trigger registries if one exists for the given block and meta + * @param block + * @param meta + * @return + */ + public static boolean hasTrigger(Block block, int meta) { + for (String modid:triggers.keySet()) { + HashMap temp = triggers.get(modid); + if (temp.containsKey(Arrays.asList(block,meta)) || + temp.containsKey(Arrays.asList(block,-1))) return true; + } + return false; + } + + /** + * modid sensitive version + */ + public static boolean hasTrigger(Block block, int meta, String modid) { + if (!triggers.containsKey(modid)) return false; + HashMap temp = triggers.get(modid); + if (temp.containsKey(Arrays.asList(block,meta)) || + temp.containsKey(Arrays.asList(block,-1))) return true; + return false; + } + + + /** + * This is called by the onItemUseFirst function in wands. + * Parameters and return value functions like you would expect for that function. + * @param world + * @param wand + * @param player + * @param x + * @param y + * @param z + * @param side + * @param block + * @param meta + * @return + */ + public static boolean performTrigger(World world, ItemStack wand, EntityPlayer player, + int x, int y, int z, int side, Block block, int meta) { + + for (String modid:triggers.keySet()) { + HashMap temp = triggers.get(modid); + List l = temp.get(Arrays.asList(block,meta)); + if (l==null) l = temp.get(Arrays.asList(block,-1)); + if (l==null) continue; + + IWandTriggerManager manager = (IWandTriggerManager) l.get(0); + int event = (Integer) l.get(1); + boolean result = manager.performTrigger(world, wand, player, x, y, z, side, event); + if (result) return true; + } + return false; + } + + /** + * modid sensitive version + */ + public static boolean performTrigger(World world, ItemStack wand, EntityPlayer player, + int x, int y, int z, int side, Block block, int meta, String modid) { + if (!triggers.containsKey(modid)) return false; + HashMap temp = triggers.get(modid); + List l = temp.get(Arrays.asList(block,meta)); + if (l==null) l = temp.get(Arrays.asList(block,-1)); + if (l==null) return false; + + IWandTriggerManager manager = (IWandTriggerManager) l.get(0); + int event = (Integer) l.get(1); + return manager.performTrigger(world, wand, player, x, y, z, side, event); + } + +} diff --git a/src/api/java/truetyper/FontHelper.java b/src/api/java/truetyper/FontHelper.java new file mode 100644 index 0000000..c83219d --- /dev/null +++ b/src/api/java/truetyper/FontHelper.java @@ -0,0 +1,122 @@ +package truetyper; + +/** +* TrueTyper: Open Source TTF implementation for Minecraft. +* Copyright (C) 2013 - Mr_okushama +* Modified by Kihira +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ScaledResolution; +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; +import org.lwjgl.util.vector.Matrix4f; + +import java.nio.FloatBuffer; +import java.util.regex.Pattern; + +public class FontHelper { + + private static final String formatEscape = "\u00A7"; + private static final Pattern formattingCodePattern = Pattern.compile("(?i)" + String.valueOf('\u00a7') + "[0-9A-FK-OR]"); + + + public static void drawString(String s, float x, float y, TrueTypeFont font, float scaleX, float scaleY, float... rgba){ + drawString(s,x,y,font,scaleX,scaleY,0,rgba); + + } + + public static void drawCenteredString(String s, float x, float y, TrueTypeFont font, float scaleX, float scaleY, float ... rgba) { + drawString(s, x - (font.getWidth(s.replaceAll(formattingCodePattern.toString(), "")) / (4F / scaleX)), y, font, scaleX, scaleY, rgba); + } + + public static void drawString(String s, float x, float y, TrueTypeFont font, float scaleX, float scaleY, float rotationZ, float... rgba){ + Minecraft mc = Minecraft.getMinecraft(); + ScaledResolution sr = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); + + if(mc.gameSettings.hideGUI){ + return; + } + float amt = 2F / sr.getScaleFactor(); + if(sr.getScaleFactor() == 1){ + amt = 2; + } + + FloatBuffer matrixData = BufferUtils.createFloatBuffer(16); + GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, matrixData); + FontHelper.set2DMode(matrixData); + GL11.glPushMatrix(); + y = mc.displayHeight-(y*sr.getScaleFactor())-(((font.getLineHeight()/amt))); + float tx = (x*sr.getScaleFactor())+(font.getWidth(s)/2); + float tranx = tx+2; + float trany = y+(font.getLineHeight()/2); + GL11.glTranslatef(tranx,trany,0); + GL11.glRotatef(rotationZ, 0f, 0f, 1f); + GL11.glTranslatef(-tranx,-trany,0); + + + GL11.glEnable(GL11.GL_BLEND); + if(s.contains(formatEscape)){ + String[] pars = s.split(formatEscape); + float totalOffset = 0; + for(int i = 0; i < pars.length; i++){ + String par = pars[i]; + float[] c = rgba; + if(i > 0){ + c = Formatter.getFormatted(par.charAt(0)); + par = par.substring(1, par.length()); + } + font.drawString((x*sr.getScaleFactor()+totalOffset), y, par, scaleX/amt, scaleY/amt, c); + totalOffset += font.getWidth(par); + } + }else{ + font.drawString((x*sr.getScaleFactor()), y, s, scaleX/amt, scaleY/amt, rgba); + } + GL11.glPopMatrix(); + GL11.glDisable(GL11.GL_BLEND); + FontHelper.set3DMode(); + } + + private static void set2DMode(FloatBuffer matrixData) { + Minecraft mc = Minecraft.getMinecraft(); + ScaledResolution sr = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); + mc.entityRenderer.setupOverlayRendering(); + GL11.glMatrixMode(GL11.GL_PROJECTION); + GL11.glPushMatrix(); + //GL11.glLoadMatrix(matrixData); + + GL11.glLoadIdentity(); + GL11.glOrtho(0, mc.displayWidth, 0, mc.displayHeight, -1, 1); + GL11.glMatrixMode(GL11.GL_MODELVIEW); + GL11.glPushMatrix(); + GL11.glLoadIdentity(); + + Matrix4f matrix = new Matrix4f(); + matrix.load(matrixData); + GL11.glTranslatef(matrix.m30*sr.getScaleFactor(),-matrix.m31*sr.getScaleFactor(), 0f); + + } + + private static void set3DMode() { + GL11.glMatrixMode(GL11.GL_MODELVIEW); + GL11.glPopMatrix(); + GL11.glMatrixMode(GL11.GL_PROJECTION); + GL11.glPopMatrix(); + Minecraft mc = Minecraft.getMinecraft(); + mc.entityRenderer.setupOverlayRendering(); + } + +} diff --git a/src/api/java/truetyper/FontLoader.java b/src/api/java/truetyper/FontLoader.java new file mode 100644 index 0000000..41ddfa4 --- /dev/null +++ b/src/api/java/truetyper/FontLoader.java @@ -0,0 +1,63 @@ +package truetyper; + +/** +* TrueTyper: Open Source TTF implementation for Minecraft. +* Copyright (C) 2013 - Mr_okushama +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +import net.minecraft.client.Minecraft; +import net.minecraft.util.ResourceLocation; + +import java.awt.*; + +public class FontLoader{ + + public static TrueTypeFont loadSystemFont(String name, float defSize, boolean antialias){ + return loadSystemFont(name, defSize, antialias, Font.TRUETYPE_FONT); + + } + + public static TrueTypeFont loadSystemFont(String name, float defSize, boolean antialias, int type){ + Font font; + TrueTypeFont out = null; + try { + font = new Font(name, type, (int)defSize); + font = font.deriveFont(defSize); + out = new TrueTypeFont(font, antialias); + } catch (Exception e) { + e.printStackTrace(); + } + return out; + } + + public static TrueTypeFont createFont(ResourceLocation res, float defSize, boolean antialias){ + return createFont(res, defSize, antialias, Font.TRUETYPE_FONT); + } + + public static TrueTypeFont createFont(ResourceLocation res, float defSize, boolean antialias, int type){ + Font font; + TrueTypeFont out = null; + try { + font = Font.createFont(type, Minecraft.getMinecraft().getResourceManager().getResource(res).getInputStream()); + font = font.deriveFont(defSize); + out = new TrueTypeFont(font, antialias); + } catch (Exception e) { + e.printStackTrace(); + } + return out; + } + +} \ No newline at end of file diff --git a/src/api/java/truetyper/Formatter.java b/src/api/java/truetyper/Formatter.java new file mode 100644 index 0000000..2489b09 --- /dev/null +++ b/src/api/java/truetyper/Formatter.java @@ -0,0 +1,86 @@ +package truetyper; + +/** +* TrueTyper: Open Source TTF implementation for Minecraft. +* Copyright (C) 2013 - Mr_okushama +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 3 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +*/ + +public class Formatter { + + public static float[] getFormatted(char c){ + int[] outrgba = null; + float[] outfloat; + switch(c){ + case '0': + outrgba = new int[]{0,0,0,0,255}; + break; + case '1': + outrgba = new int[]{0,0,170,255}; + break; + case '2': + outrgba = new int[]{0,170,0,255}; + break; + case '3': + outrgba = new int[]{0,170,170,255}; + break; + case '4': + outrgba = new int[]{170,0,0,255}; + break; + case '5': + outrgba = new int[]{170,0,170,255}; + break; + case '6': + outrgba = new int[]{255,170,0,255}; + break; + case '7': + outrgba = new int[]{170,170,170,255}; + break; + case '8': + outrgba = new int[]{85,85,85,255}; + break; + case '9': + outrgba = new int[]{85,85,255,255}; + break; + case 'a': + outrgba = new int[]{85,255,85,255}; + break; + case 'b': + outrgba = new int[]{85,255,255,255}; + break; + case 'c': + outrgba = new int[]{255,85,85,255}; + break; + case 'd': + outrgba = new int[]{85,255,255,255}; + break; + case 'e': + outrgba = new int[]{255,255,85,255}; + break; + case 'f': + outrgba = new int[]{255,255,255,255}; + break; + default: + outrgba = new int[]{255,255,255,255}; + break; + } + outfloat = new float[outrgba.length]; + for(int i = 0; i < outrgba.length ; i++){ + outfloat[i] = outrgba[i] > 0 ? outrgba[i]/255 : 0; + } + return outfloat; + } + +} diff --git a/src/api/java/truetyper/LICENSE.md b/src/api/java/truetyper/LICENSE.md new file mode 100644 index 0000000..20d40b6 --- /dev/null +++ b/src/api/java/truetyper/LICENSE.md @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/src/api/java/truetyper/README.md b/src/api/java/truetyper/README.md new file mode 100644 index 0000000..1c13fa7 --- /dev/null +++ b/src/api/java/truetyper/README.md @@ -0,0 +1,51 @@ +An open TrueType Font renderer for Minecraft + +USAGE GUIDE! + +Create a TrueTypeFont object variable using either a resource location to a .ttf or +name of a system font, choose base size and whether or not you want anti aliasing like so: + +testFont = FontLoader.createFont(new ResourceLocation("modid", "testfont.ttf"), 24f, false); + +testFont2 = FontLoader.loadSystemFont("Arial", 16f, false); + + + +Once the font has been created, to render to screen, simply use the static call: + +FontHelper.drawString(String name, float x, float y, TrueTypeFont font, float scaleX, float scaleY, float... rgba) + +eg, +FontHelper.drawString(windowTitle, posX, posY, testFont, 1f, 1f); + + +Thanks to ProfMobius for tweaking the matrix ( http://www.twitter.com/ProfMobius ) + + ---- + + CHANGELOG: + 10/11/2013 - Added support for minecraft's colour code formatting ( § / \u00A7) - I personally use net.minecraft.util.EnumChatFormatting in my strings + + ---- + +TrueTyper in part, is modified from Slick2d, full details on their licensing can be found here: + +http://slick.ninjacave.com/license/ + + ---- + +TrueTyper: Open Source TTF implementation for Minecraft. +Copyright (C) 2013 - Mr_okushama ( http://www.twitter.com/Mr_okushama ) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . \ No newline at end of file diff --git a/src/api/java/truetyper/TrueTypeFont.java b/src/api/java/truetyper/TrueTypeFont.java new file mode 100644 index 0000000..e384bc2 --- /dev/null +++ b/src/api/java/truetyper/TrueTypeFont.java @@ -0,0 +1,496 @@ +package truetyper; + +import net.minecraft.client.renderer.Tessellator; +import org.lwjgl.BufferUtils; +import org.lwjgl.opengl.GL11; +import org.lwjgl.util.glu.GLU; + +import java.awt.*; +import java.awt.image.BufferedImage; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.DataBufferInt; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.util.HashMap; +import java.util.Map; + + +/** +* TrueTyper: Open Source TTF implementation for Minecraft. +* Modified from Slick2D - under BSD Licensing - http://slick.ninjacave.com/license/ +* +* Copyright (c) 2013 - Slick2D +* +* All rights reserved. +*/ + +public class TrueTypeFont { + public final static int + ALIGN_LEFT = 0, + ALIGN_RIGHT = 1, + ALIGN_CENTER = 2; + /** Array that holds necessary information about the font characters */ + private FloatObject[] charArray = new FloatObject[256]; + + /** Map of user defined font characters (Character <-> IntObject) */ + private Map customChars = new HashMap(); + + /** Boolean flag on whether AntiAliasing is enabled or not */ + protected boolean antiAlias; + + /** Font's size */ + private float fontSize = 0; + + /** Font's height */ + private float fontHeight = 0; + + /** Texture used to cache the font 0-255 characters */ + private int fontTextureID; + + /** Default font texture width */ + private int textureWidth = 1024; + + /** Default font texture height */ + private int textureHeight = 1024; + + /** A reference to Java's AWT Font that we create our font texture from */ + protected Font font; + + /** The font metrics for our Java AWT font */ + private FontMetrics fontMetrics; + + + private int correctL = 9, correctR = 8; + + private class FloatObject { + /** Character's width */ + public float width; + + /** Character's height */ + public float height; + + /** Character's stored x position */ + public float storedX; + + /** Character's stored y position */ + public float storedY; + } + + + public TrueTypeFont(Font font, boolean antiAlias, char[] additionalChars) { + this.font = font; + this.fontSize = font.getSize()+3; + this.antiAlias = antiAlias; + + createSet( additionalChars ); + System.out.println("TrueTypeFont loaded: "+font+" - AntiAlias = "+antiAlias); + fontHeight -= 1; + if (fontHeight <= 0) fontHeight = 1; + } + + public TrueTypeFont(Font font, boolean antiAlias) { + this( font, antiAlias, null ); + } + public void setCorrection(boolean on) { + if (on) { + correctL = 2; + correctR = 1; + } else { + correctL = 0; + correctR = 0; + } + } + + private BufferedImage getFontImage(char ch) { + // Create a temporary image to extract the character's size + BufferedImage tempfontImage = new BufferedImage(1, 1, + BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D) tempfontImage.getGraphics(); + if (antiAlias == true) { + g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + } + g.setFont(font); + fontMetrics = g.getFontMetrics(); + float charwidth = fontMetrics.charWidth(ch)+8; + + if (charwidth <= 0) { + charwidth = 7; + } + float charheight = fontMetrics.getHeight()+3; + if (charheight <= 0) { + charheight = fontSize; + } + + // Create another image holding the character we are creating + BufferedImage fontImage; + fontImage = new BufferedImage((int)charwidth,(int)charheight, + BufferedImage.TYPE_INT_ARGB); + Graphics2D gt = (Graphics2D) fontImage.getGraphics(); + if (antiAlias == true) { + gt.setRenderingHint(RenderingHints.KEY_ANTIALIASING, + RenderingHints.VALUE_ANTIALIAS_ON); + } + gt.setFont(font); + + gt.setColor(Color.WHITE); + int charx = 3; + int chary = 1; + gt.drawString(String.valueOf(ch), (charx), (chary) + + fontMetrics.getAscent()); + + return fontImage; + + } + + private void createSet( char[] customCharsArray ) { + // If there are custom chars then I expand the font texture twice + if (customCharsArray != null && customCharsArray.length > 0) { + textureWidth *= 2; + } + + // In any case this should be done in other way. Texture with size 512x512 + // can maintain only 256 characters with resolution of 32x32. The texture + // size should be calculated dynamicaly by looking at character sizes. + + try { + + BufferedImage imgTemp = new BufferedImage(textureWidth, textureHeight, BufferedImage.TYPE_INT_ARGB); + Graphics2D g = (Graphics2D) imgTemp.getGraphics(); + + g.setColor(new Color(0,0,0,1)); + g.fillRect(0,0,textureWidth,textureHeight); + + float rowHeight = 0; + float positionX = 0; + float positionY = 0; + + int customCharsLength = ( customCharsArray != null ) ? customCharsArray.length : 0; + + for (int i = 0; i < 256 + customCharsLength; i++) { + + // get 0-255 characters and then custom characters + char ch = ( i < 256 ) ? (char) i : customCharsArray[i-256]; + + BufferedImage fontImage = getFontImage(ch); + + FloatObject newIntObject = new FloatObject(); + + newIntObject.width = fontImage.getWidth(); + newIntObject.height = fontImage.getHeight(); + + if (positionX + newIntObject.width >= textureWidth) { + positionX = 0; + positionY += rowHeight; + rowHeight = 0; + } + + newIntObject.storedX = positionX; + newIntObject.storedY = positionY; + + if (newIntObject.height > fontHeight) { + fontHeight = newIntObject.height; + } + + if (newIntObject.height > rowHeight) { + rowHeight = newIntObject.height; + } + + // Draw it here + g.drawImage(fontImage, (int)positionX, (int)positionY, null); + + positionX += newIntObject.width; + + if( i < 256 ) { // standard characters + charArray[i] = newIntObject; + } else { // custom characters + customChars.put( new Character( ch ), newIntObject ); + } + + fontImage = null; + } + + fontTextureID = loadImage(imgTemp); + + + + //.getTexture(font.toString(), imgTemp); + + } catch (Exception e) { + System.err.println("Failed to create font."); + e.printStackTrace(); + } + } + + private void drawQuad(float drawX, float drawY, float drawX2, float drawY2, + float srcX, float srcY, float srcX2, float srcY2) { + float DrawWidth = drawX2 - drawX; + float DrawHeight = drawY2 - drawY; + float TextureSrcX= srcX / textureWidth; + float TextureSrcY = srcY / textureHeight; + float SrcWidth = srcX2 - srcX; + float SrcHeight = srcY2 - srcY; + float RenderWidth = (SrcWidth / textureWidth); + float RenderHeight = (SrcHeight / textureHeight); + Tessellator t = Tessellator.instance; + + //t.setColorRGBA_F(0f, 0f, 0f, 1f); + + t.addVertexWithUV(drawX, drawY, 0, TextureSrcX, TextureSrcY); + //GL11.glTexCoord2f(TextureSrcX, TextureSrcY); + //GL11.glVertex2f(drawX, drawY); + + t.addVertexWithUV(drawX, drawY + DrawHeight, 0, TextureSrcX, TextureSrcY + RenderHeight); + //GL11.glTexCoord2f(TextureSrcX, TextureSrcY + RenderHeight); + //GL11.glVertex2f(drawX, drawY + DrawHeight); + + t.addVertexWithUV(drawX + DrawWidth, drawY + DrawHeight, 0, TextureSrcX + RenderWidth, TextureSrcY + RenderHeight); + //GL11.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY + RenderHeight); + //GL11.glVertex2f(drawX + DrawWidth, drawY + DrawHeight); + + t.addVertexWithUV(drawX + DrawWidth, drawY, 0, TextureSrcX + RenderWidth, TextureSrcY); + //GL11.glTexCoord2f(TextureSrcX + RenderWidth, TextureSrcY); + //GL11.glVertex2f(drawX + DrawWidth, drawY); + } + + public float getWidth(String whatchars) { + float totalwidth = 0; + FloatObject floatObject = null; + int currentChar = 0; + float lastWidth = -10f; + for (int i = 0; i < whatchars.length(); i++) { + currentChar = whatchars.charAt(i); + if (currentChar < 256) { + floatObject = charArray[currentChar]; + } else { + floatObject = (FloatObject)customChars.get( new Character( (char) currentChar ) ); + } + + if( floatObject != null ){ + totalwidth += floatObject.width/2; + lastWidth = floatObject.width; + } + } + //System.out.println("Size: "+totalwidth); + return this.fontMetrics.stringWidth(whatchars); + //return (totalwidth); + } + + public float getHeight() { + return fontHeight; + } + + + public float getHeight(String HeightString) { + return fontHeight; + } + + public float getLineHeight() { + return fontHeight; + } + + public void drawString(float x, float y, String whatchars, float scaleX, float scaleY, float... rgba) { + if(rgba.length == 0) rgba = new float[]{1f,1f,1f,1f}; + drawString(x,y,whatchars, 0, whatchars.length()-1, scaleX, scaleY, ALIGN_LEFT, rgba); + } + public void drawString(float x, float y, String whatchars, float scaleX, float scaleY, int format, float... rgba) { + if(rgba.length == 0) rgba = new float[]{1f,1f,1f,1f}; + + drawString(x,y,whatchars, 0, whatchars.length()-1, scaleX, scaleY, format, rgba); + } + + + public void drawString(float x, float y, String whatchars, int startIndex, int endIndex, float scaleX, float scaleY, int format, float... rgba) { + if(rgba.length == 0) rgba = new float[]{1f,1f,1f,1f}; + GL11.glPushMatrix(); + GL11.glScalef (scaleX, scaleY, 1.0f); + + FloatObject floatObject = null; + int charCurrent; + + + float totalwidth = 0; + int i = startIndex, d, c; + float startY = 0; + + + + switch (format) { + case ALIGN_RIGHT: { + d = -1; + c = correctR; + + while (i < endIndex) { + if (whatchars.charAt(i) == '\n') startY -= fontHeight; + i++; + } + break; + } + case ALIGN_CENTER: { + for (int l = startIndex; l <= endIndex; l++) { + charCurrent = whatchars.charAt(l); + if (charCurrent == '\n') break; + if (charCurrent < 256) { + floatObject = charArray[charCurrent]; + } else { + floatObject = (FloatObject)customChars.get( new Character( (char) charCurrent ) ); + } + totalwidth += floatObject.width-correctL; + } + totalwidth /= -2; + } + case ALIGN_LEFT: + default: { + d = 1; + c = correctL; + break; + } + + } + GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureID); + Tessellator t = Tessellator.instance; + t.startDrawingQuads(); + // GL11.glBegin(GL11.GL_QUADS); + if(rgba.length == 4) t.setColorRGBA_F(rgba[0], rgba[1], rgba[2], rgba[3]); + while (i >= startIndex && i <= endIndex) { + + charCurrent = whatchars.charAt(i); + if (charCurrent < 256) { + floatObject = charArray[charCurrent]; + } else { + floatObject = (FloatObject)customChars.get( new Character( (char) charCurrent ) ); + } + + if( floatObject != null ) { + if (d < 0) totalwidth += (floatObject.width-c) * d; + if (charCurrent == '\n') { + startY -= fontHeight * d; + totalwidth = 0; + if (format == ALIGN_CENTER) { + for (int l = i+1; l <= endIndex; l++) { + charCurrent = whatchars.charAt(l); + if (charCurrent == '\n') break; + if (charCurrent < 256) { + floatObject = charArray[charCurrent]; + } else { + floatObject = (FloatObject)customChars.get( new Character( (char) charCurrent ) ); + } + totalwidth += floatObject.width-correctL; + } + totalwidth /= -2; + } + //if center get next lines total width/2; + } + else { + drawQuad((totalwidth + floatObject.width) + x/scaleX, + startY + y/scaleY, + totalwidth + x/scaleX, + (startY + floatObject.height) + y/scaleY, + floatObject.storedX + floatObject.width, + floatObject.storedY + floatObject.height, + floatObject.storedX, + floatObject.storedY); + if (d > 0) totalwidth += (floatObject.width-c) * d ; + } + i += d; + + } + } + t.draw(); + // GL11.glEnd(); + + GL11.glPopMatrix(); + } + public static int loadImage(BufferedImage bufferedImage) { + try { + short width = (short)bufferedImage.getWidth(); + short height = (short)bufferedImage.getHeight(); + //textureLoader.bpp = bufferedImage.getColorModel().hasAlpha() ? (byte)32 : (byte)24; + int bpp = (byte)bufferedImage.getColorModel().getPixelSize(); + ByteBuffer byteBuffer; + DataBuffer db = bufferedImage.getData().getDataBuffer(); + if (db instanceof DataBufferInt) { + int intI[] = ((DataBufferInt)(bufferedImage.getData().getDataBuffer())).getData(); + byte newI[] = new byte[intI.length * 4]; + for (int i = 0; i < intI.length; i++) { + byte b[] = intToByteArray(intI[i]); + int newIndex = i*4; + + newI[newIndex] = b[1]; + newI[newIndex+1] = b[2]; + newI[newIndex+2] = b[3]; + newI[newIndex+3] = b[0]; + } + + byteBuffer = ByteBuffer.allocateDirect( + width*height*(bpp/8)) + .order(ByteOrder.nativeOrder()) + .put(newI); + } else { + byteBuffer = ByteBuffer.allocateDirect( + width*height*(bpp/8)) + .order(ByteOrder.nativeOrder()) + .put(((DataBufferByte)(bufferedImage.getData().getDataBuffer())).getData()); + } + byteBuffer.flip(); + + + int internalFormat = GL11.GL_RGBA8, + format = GL11.GL_RGBA; + IntBuffer textureId = BufferUtils.createIntBuffer(1);; + GL11.glGenTextures(textureId); + GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId.get(0)); + + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP); + + + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); + GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); + //GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); + //GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_NEAREST); + + //GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_LINEAR); + //GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST_MIPMAP_LINEAR); + //GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST_MIPMAP_NEAREST); + + GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE); + + GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, internalFormat, width, height, format, GL11.GL_UNSIGNED_BYTE, byteBuffer); + return textureId.get(0); + + } catch (Exception e) { + e.printStackTrace(); + System.exit(-1); + } + + return -1; + } + public static boolean isSupported(String fontname) { + Font font[] = getFonts(); + for (int i = font.length-1; i >= 0; i--) { + if (font[i].getName().equalsIgnoreCase(fontname)) + return true; + } + return false; + } + public static Font[] getFonts() { + return GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts(); + } + public static byte[] intToByteArray(int value) { + return new byte[] { + (byte)(value >>> 24), + (byte)(value >>> 16), + (byte)(value >>> 8), + (byte)value}; + } + + public void destroy() { + IntBuffer scratch = BufferUtils.createIntBuffer(1); + scratch.put(0, fontTextureID); + GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0); + GL11.glDeleteTextures(scratch); + } +} \ No newline at end of file diff --git a/src/main/java/fox/spiteful/forbidden/Config.java b/src/main/java/fox/spiteful/forbidden/Config.java index d337cae..ee1e549 100644 --- a/src/main/java/fox/spiteful/forbidden/Config.java +++ b/src/main/java/fox/spiteful/forbidden/Config.java @@ -26,8 +26,6 @@ public class Config { public static int bloodSealPotionID = 70; - public static int thaumcraftTaintBiomeID; - public static int thaumcraftTaintPotionID; public static ItemStack thaumcraftResource; public static ItemStack thaumcraftTaintBlock; public static ItemStack thaumcraftOre; @@ -87,9 +85,6 @@ public class Config { thaumcraftTaintBlock = ItemApi.getBlock("blockTaint", 0); thaumcraftOre = ItemApi.getBlock("blockCustomOre", 0); taintMaterial = Block.getBlockFromItem(thaumcraftTaintBlock.getItem()).getMaterial(); - - thaumcraftTaintPotionID = Class.forName("thaumcraft.common.config.Config").getField("potionFluxTaintID").getInt(null); - thaumcraftTaintBiomeID = Class.forName("thaumcraft.common.config.Config").getField("biomeTaintID").getInt(null); } catch (Exception e) { LogHandler.log(Level.ERROR, e, "There was problem when retrieving information from Thaumcraft."); e.printStackTrace(); diff --git a/src/main/java/fox/spiteful/forbidden/compat/BloodMagic.java b/src/main/java/fox/spiteful/forbidden/compat/BloodMagic.java index d426029..7cf9ece 100644 --- a/src/main/java/fox/spiteful/forbidden/compat/BloodMagic.java +++ b/src/main/java/fox/spiteful/forbidden/compat/BloodMagic.java @@ -66,8 +66,8 @@ public class BloodMagic { IArcaneRecipe blood_staff = ThaumcraftApi.addArcaneCraftingRecipe("ROD_blood_staff", new ItemStack(ForbiddenItems.wandCore, 1, 9), (new AspectList()).add(Aspect.ENTROPY, 26).add(Aspect.FIRE, 26).add(Aspect.WATER, 26).add(Aspect.AIR, 26).add(Aspect.EARTH, 26).add(Aspect.ORDER, 26), new Object[] { "__D", "_B_", "B__", Character.valueOf('B'), new ItemStack(ForbiddenItems.wandCore, 1, 3), Character.valueOf('D'), new ItemStack(demonShard) }); (new DarkResearchItem("ROD_blood_staff", "FORBIDDEN", "[BM]", (new AspectList()).add(Aspect.LIFE, 8).add(Aspect.WATER, 7).add(Aspect.WEAPON, 6), -5, -7, 5, new ItemStack(ForbiddenItems.wandCore, 1, 9))).setPages(new ResearchPage[] { new ResearchPage("forbidden.research_page.ROD_blood_staff.1"), new ResearchPage(blood_staff) }).setParents(new String[] { "ROD_silverwood_staff", "ROD_blood" }).setSpecial().setConcealed().registerResearchItem(); - InfusionRecipe blood_rapier = ThaumcraftApi.addInfusionCraftingRecipe("BLOODRAPIER", new ItemStack(ForbiddenItems.bloodRapier, 1, 0), 8, (new AspectList()).add(Aspect.LIFE, 18).add(Aspect.HUNGER, 32).add(Aspect.WEAPON, 8), ItemApi.getItem("itemSwordThaumium", 0), new ItemStack[] { ItemApi.getItem("itemResource", 12), new ItemStack(ForbiddenItems.deadlyShards, 1, 0), new ItemStack(ForbiddenItems.gluttonyShard, 1, 0), new ItemStack(Items.feather), ItemApi.getItem("itemResource", 6) }); - (new DarkResearchItem("BLOODRAPIER", "FORBIDDEN", "[BM]", (new AspectList()).add(Aspect.LIFE, 16).add(Aspect.HUNGER, 12).add(Aspect.WEAPON, 12).add(Aspect.MAGIC, 12), -9, 8, 4, new ItemStack(ForbiddenItems.bloodRapier, 1, 0))).setPages(new ResearchPage[] { new ResearchPage("forbidden.research_page.BLOODRAPIER.1"), new ResearchPage(blood_rapier) }).setParents(new String[] { "INFAUXSION" }).setConcealed().registerResearchItem(); + InfusionRecipe blood_rapier = ThaumcraftApi.addInfusionCraftingRecipe("BLOODRAPIER", new ItemStack(ForbiddenItems.bloodRapier, 1, 0), 8, (new AspectList()).add(Aspect.LIFE, 18).add(Aspect.HUNGER, 32).add(Aspect.WEAPON, 8), ItemApi.getItem("itemSwordVoid", 0), new ItemStack[] { ItemApi.getItem("itemResource", 12), new ItemStack(ForbiddenItems.deadlyShards, 1, 0), new ItemStack(ForbiddenItems.gluttonyShard, 1, 0), new ItemStack(Items.feather), ItemApi.getItem("itemResource", 6) }); + (new DarkResearchItem("BLOODRAPIER", "FORBIDDEN", "[BM]", (new AspectList()).add(Aspect.LIFE, 16).add(Aspect.HUNGER, 12).add(Aspect.WEAPON, 12).add(Aspect.MAGIC, 12), -9, 8, 4, new ItemStack(ForbiddenItems.bloodRapier, 1, 0))).setPages(new ResearchPage[] { new ResearchPage("forbidden.research_page.BLOODRAPIER.1"), new ResearchPage(blood_rapier) }).setParents(new String[] { "INFAUXSION", "VOIDMETAL" }).setConcealed().registerResearchItem(); ThaumcraftApi.addWarpToResearch("BLOODRAPIER", 3); if (Compat.kami) { diff --git a/src/main/java/fox/spiteful/forbidden/compat/Compat.java b/src/main/java/fox/spiteful/forbidden/compat/Compat.java index ca0e0f4..7d04356 100644 --- a/src/main/java/fox/spiteful/forbidden/compat/Compat.java +++ b/src/main/java/fox/spiteful/forbidden/compat/Compat.java @@ -73,7 +73,7 @@ public class Compat { if (kami) { try { - Item kamiResource = GameRegistry.findItem("ThaumicTinkerer", "ttinkerer:kamiResource"); + Item kamiResource = GameRegistry.findItem("ThaumicTinkerer", "kamiResource"); list = new AspectList().add(DarkAspects.NETHER, 2).add(Aspect.MAGIC, 1).add(Aspect.CRYSTAL, 1); ThaumcraftApi.registerObjectTag(new ItemStack(kamiResource, 1, 6), list); list = new AspectList().add(Aspect.ELDRITCH, 2).add(Aspect.MAGIC, 1).add(Aspect.CRYSTAL, 1); @@ -115,14 +115,16 @@ public class Compat { i = GameRegistry.findItemStack("Natura", "soil.tainted", 1); if (i != null) { - list = ThaumcraftApiHelper.getObjectAspects(new ItemStack(i.getItem(), 1, 0)); + //list = ThaumcraftApiHelper.getObjectAspects(new ItemStack(i.getItem(), 1, 0)); + list = new AspectList().add(Aspect.EARTH, 1); list.add(DarkAspects.NETHER, 1); ThaumcraftApi.registerObjectTag(new ItemStack(i.getItem(), 1, OreDictionary.WILDCARD_VALUE), list); } i = GameRegistry.findItemStack("Natura", "heatsand", 1); if (i != null) { - list = ThaumcraftApiHelper.getObjectAspects(new ItemStack(i.getItem(), 1, 0)); + //list = ThaumcraftApiHelper.getObjectAspects(new ItemStack(i.getItem(), 1, 0)); + list = new AspectList().add(Aspect.FIRE, 1).add(Aspect.EARTH, 1); list.add(DarkAspects.NETHER, 1); ThaumcraftApi.registerObjectTag(new ItemStack(i.getItem(), 1, OreDictionary.WILDCARD_VALUE), list); } diff --git a/src/main/java/fox/spiteful/forbidden/items/ItemTaintShovel.java b/src/main/java/fox/spiteful/forbidden/items/ItemTaintShovel.java index 9643d23..005a5dc 100644 --- a/src/main/java/fox/spiteful/forbidden/items/ItemTaintShovel.java +++ b/src/main/java/fox/spiteful/forbidden/items/ItemTaintShovel.java @@ -16,6 +16,8 @@ import fox.spiteful.forbidden.Config; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import thaumcraft.common.Thaumcraft; +import thaumcraft.common.config.ConfigBlocks; public class ItemTaintShovel extends ItemSpade implements IRepairable { @@ -63,17 +65,31 @@ public class ItemTaintShovel extends ItemSpade implements IRepairable @Override public boolean onItemUse(ItemStack itemstack, EntityPlayer player, World world, int x, int y, int z, int side, float par8, float par9, float par10) { - if(player != null && player.isPotionActive(Config.thaumcraftTaintPotionID)) { - itemstack.damageItem(5, player); - player.swingItem(); - if(player.worldObj.isRemote) - player.removePotionEffectClient(Config.thaumcraftTaintPotionID); - else { - world.playSoundEffect((double)x + 0.5D, (double)y + 0.5D, (double)z + 0.5D, "thaumcraft:wandfail", 0.2F, 0.2F + world.rand.nextFloat() * 0.2F); - player.removePotionEffect(Config.thaumcraftTaintPotionID); - } - return true; - } - return super.onItemUse(itemstack, player, world, x, y, z, side, par8, par9, par10); + int purified = 0; + for(int ex = x - 5;ex < x + 6;ex++){ + for(int wy = y - 4;wy < y + 5;wy++){ + for(int zee = z - 5;zee < z + 6;zee++){ + Block target = world.getBlock(ex, wy, zee); + if(target != null && (target == ConfigBlocks.blockFluxGoo || target == ConfigBlocks.blockFluxGas)){ + purified++; + world.setBlockToAir(ex, wy, zee); + for(int sparkle = 0;sparkle < 3;sparkle++) { + double d1 = (double) ((float) ex + world.rand.nextFloat()); + double d2 = (double) ((float) wy + world.rand.nextFloat()); + double d0 = (double) ((float) zee + world.rand.nextFloat()); + Thaumcraft.proxy.sparkle((float) d1, (float) d2, (float) d0, 0x76FFFB); + } + } + } + } + } + if(purified > 0){ + itemstack.damageItem(Math.min(purified, 15), player); + player.swingItem(); + world.playSoundEffect((double)x + 0.5D, (double)y + 0.5D, (double)z + 0.5D, "thaumcraft:wandfail", 0.2F, 0.2F + world.rand.nextFloat() * 0.2F); + return true; + } + else + return super.onItemUse(itemstack, player, world, x, y, z, side, par8, par9, par10); } } diff --git a/src/main/java/fox/spiteful/forbidden/items/wands/TaintedWandUpdate.java b/src/main/java/fox/spiteful/forbidden/items/wands/TaintedWandUpdate.java index 0b06051..86f7452 100644 --- a/src/main/java/fox/spiteful/forbidden/items/wands/TaintedWandUpdate.java +++ b/src/main/java/fox/spiteful/forbidden/items/wands/TaintedWandUpdate.java @@ -13,7 +13,7 @@ public class TaintedWandUpdate implements IWandRodOnUpdate { Aspect primals[] = Aspect.getPrimalAspects().toArray(new Aspect[0]); public void onUpdate(ItemStack itemstack, EntityPlayer player) { - if(player.ticksExisted % 100 == 0 && player.worldObj.getBiomeGenForCoords((int)player.posX, (int)player.posZ).biomeID == Config.thaumcraftTaintBiomeID){ + if(player.ticksExisted % 100 == 0 && player.worldObj.getBiomeGenForCoords((int)player.posX, (int)player.posZ).biomeID == thaumcraft.common.config.Config.biomeTaintID){ for(int x = 0;x < primals.length;x++){ if(((ItemWandCasting)itemstack.getItem()).getVis(itemstack, primals[x]) < ((ItemWandCasting)itemstack.getItem()).getMaxVis(itemstack) / 10) { ((ItemWandCasting)itemstack.getItem()).addVis(itemstack, primals[x], 1, true); diff --git a/src/main/java/fox/spiteful/forbidden/items/wands/YandereWandUpdate.java b/src/main/java/fox/spiteful/forbidden/items/wands/YandereWandUpdate.java index 7607800..a960d78 100644 --- a/src/main/java/fox/spiteful/forbidden/items/wands/YandereWandUpdate.java +++ b/src/main/java/fox/spiteful/forbidden/items/wands/YandereWandUpdate.java @@ -23,7 +23,7 @@ public class YandereWandUpdate implements IWandRodOnUpdate { if(((ItemWandCasting)itemstack.getItem()).getCap(itemstack).getTag().equals("manasteel")) cost = 900; else - cost = 1000; + cost = 1100; for(int x = 0;x < primals.length;x++) { diff --git a/src/main/resources/assets/forbidden/lang/en_US.lang b/src/main/resources/assets/forbidden/lang/en_US.lang index ab4c8ec..2b51d1e 100644 --- a/src/main/resources/assets/forbidden/lang/en_US.lang +++ b/src/main/resources/assets/forbidden/lang/en_US.lang @@ -41,7 +41,7 @@ item.Crystalwell.name=Crystal Scribing Tools item.RidingCrop.name=Riding Crop item.Divinewell.name=Scribing Tools of Thoth item.BoundShears.name=Bound Shears -item.BloodRapier.name=Strix Rapier +item.BloodRapier.name=Rapier of the Strix # # Wand components item.Wand.tainted.rod=Tainted @@ -115,7 +115,7 @@ forbidden.research_page.ARCANECAKE.1=If there's one thing you love, it's cake. Y # forbidden.research_name.TAINTSHOVEL=Shovel of the Purifier forbidden.research_text.TAINTSHOVEL=Like a hot knife through really gross butter -forbidden.research_page.TAINTSHOVEL.1=Sometimes, the easiest way to remove patches of taint is to just physically remove them.

By reinforcing a thaumium shovel with Silverwood and Order Shards, you have created a tool capable of combating Taint. The Shovel of the Purifier can dig through pulpy tainted lands as effectively as it can through normal earth. If held in your hand and right-clicked, the shovel cures you of Flux Taint at the cost of durability. +forbidden.research_page.TAINTSHOVEL.1=Sometimes, the easiest way to remove patches of taint is to just physically remove them.

By reinforcing a thaumium shovel with Silverwood and Order Shards, you have created a tool capable of combating Taint. The Shovel of the Purifier can dig through pulpy tainted lands as effectively as it can through normal earth.

If held in your hand and right-clicked, the shovel will purify all flux goo and gas from a small radius at the cost of durability. You no longer have to clean up infusion mishaps with a piece of cobblestone! forbidden.research_page.TAINTSHOVEL.2=When digging through taint with the shovel, there's a chance that the twisted energies released will crystallize into Taint Shards. Unlike other tainted materials, the crystalline pattern keeps these shards stable enough to carry around. # forbidden.research_name.TAINTPICK=Pickaxe of Distortion @@ -240,9 +240,9 @@ forbidden.research_name.DIVINEORB=Transcendent Blood Orb forbidden.research_text.DIVINEORB=Orb of Armok forbidden.research_page.DIVINEORB.1=It is whispered among some Thaumaturgic circles that Ichor is actually the blood of the divine. You don't know whether this is true or not, but Ichor does seem to have an affinity with more advanced alchemical devices.

The Transcendent Blood Orb is what happens when you combine the most advanced Thaumaturgy and Alchemical Wizardry available. The infusion vastly increases the orb's carrying capacity, allowing it to hold a whopping 70,000,000 LP. What would you ever need that much LP for? # -forbidden.research_name.BLOODRAPIER=Strix Rapier +forbidden.research_name.BLOODRAPIER=Rapier of the Strix forbidden.research_text.BLOODRAPIER=Destroyer of Blood Magic -forbidden.research_page.BLOODRAPIER.1=There's nothing more dangerous than a Blood Magus that is gorged on life essence. As long as they have enough life energy, there's very little that can pierce their defenses.

Until now.

The Strix Rapier, upon striking someone, drinks LP directly from their Soul Network and seals it so that they can't regain any more LP for a while. It may not do very much physical damage, but it packs a metaphysical punch against Blood Magi. +forbidden.research_page.BLOODRAPIER.1=There's nothing more dangerous than a Blood Magus that is gorged on life essence. As long as they have enough life energy, there's very little that can pierce their defenses.

Until now.

The Rapier of the Strix, upon striking someone, drinks LP directly from their Soul Network and seals it so that they can't regain any more LP for a while. It may not do very much physical damage, but it packs a metaphysical punch against Blood Magi. # forbidden.research_name.CRYSTALWELL=Crystal Scribing Tools forbidden.research_text.CRYSTALWELL=Arcane Research Grant