-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathThaumcraftApi.java
446 lines (401 loc) · 18.4 KB
/
ThaumcraftApi.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
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.EnumArmorMaterial;
import net.minecraft.item.EnumToolMaterial;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTBase;
import net.minecraftforge.common.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 EnumToolMaterial toolMatThaumium = EnumHelper.addToolMaterial("THAUMIUM", 3, 400, 7F, 2, 22);
public static EnumToolMaterial toolMatElemental = EnumHelper.addToolMaterial("THAUMIUM_ELEMENTAL", 3, 1500, 10F, 3, 18);
public static EnumArmorMaterial armorMatThaumium = EnumHelper.addArmorMaterial("THAUMIUM", 25, new int[] { 2, 6, 5, 2 }, 25);
public static EnumArmorMaterial armorMatSpecial = EnumHelper.addArmorMaterial("SPECIAL", 25, new int[] { 1, 3, 2, 1 }, 25);
//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<Integer> portableHoleBlackList = new ArrayList<Integer>();
//RESEARCH/////////////////////////////////////////
public static ArrayList<IScanEventHandler> scanEventhandlers = new ArrayList<IScanEventHandler>();
public static ArrayList<EntityTags> scanEntities = new ArrayList<EntityTags>();
public static class EntityTags {
public EntityTags(String entityName, NBTBase[] nbts, AspectList aspects) {
this.entityName = entityName;
this.nbts = nbts;
this.aspects = aspects;
}
public String entityName;
public NBTBase[] 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. <br>For example the normal and wither skeleton:
* <br>ThaumcraftApi.registerEntityTag("Skeleton", (new AspectList()).add(Aspect.DEATH, 5));
* <br>ThaumcraftApi.registerEntityTag("Skeleton", (new AspectList()).add(Aspect.DEATH, 8), new NBTTagByte("SkeletonType",(byte) 1));
*/
public static void registerEntityTag(String entityName, AspectList aspects, NBTBase... nbt ) {
scanEntities.add(new EntityTags(entityName,nbt,aspects));
}
//RECIPES/////////////////////////////////////////
private static ArrayList craftingRecipes = new ArrayList();
private static HashMap<Object,ItemStack> smeltingBonus = new HashMap<Object,ItemStack>();
/**
* 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.itemID,in.getItemDamage()),
new ItemStack(out.itemID,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.itemID,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.itemID,in.getItemDamage()));
if (out==null) {
String od = OreDictionary.getOreName( OreDictionary.getOreID(in));
out = smeltingBonus.get(od);
}
return out;
}
@Deprecated
private static ArrayList<List> smeltingBonusExlusion = new ArrayList<List>();
/**
* DOES NOTHING ANYMORE - WILL REMOVE NEXT MAJOR VERSION
*/
@Deprecated
public static void addSmeltingBonusExclusion(ItemStack in) {}
/**
* DOES NOTHING ANYMORE - WILL REMOVE NEXT MAJOR VERSION
*/
@Deprecated
public static boolean isSmeltingBonusExluded(ItemStack in) {return false;}
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 NBTBase)) 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).recipeOutput instanceof ItemStack) {
if (((ItemStack) ((InfusionRecipe)r).recipeOutput).isItemEqual(res))
return (InfusionRecipe)r;
}
}
}
return null;
}
/**
* @param key the research key required for this recipe to work.
* @param result the output result
* @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).recipeOutput.isItemEqual(stack))
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<int[],Object[]> keyCache = new HashMap<int[],Object[]>();
public static Object[] getCraftingRecipeKey(EntityPlayer player, ItemStack stack) {
int[] key = new int[] {stack.itemID,stack.getItemDamage()};
if (keyCache.containsKey(key)) {
if (keyCache.get(key)==null) return null;
if (ThaumcraftApiHelper.isResearchComplete(player.username, (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<ri.getPages().length;a++) {
ResearchPage page = ri.getPages()[a];
if (page.recipeOutput!=null && stack !=null && page.recipeOutput.isItemEqual(stack)) {
keyCache.put(key,new Object[] {ri.key,a});
if (ThaumcraftApiHelper.isResearchComplete(player.username, ri.key))
return new Object[] {ri.key,a};
else
return null;
}
}
}
}
keyCache.put(key,null);
return null;
}
//ASPECTS////////////////////////////////////////
public static ConcurrentHashMap<List,AspectList> objectTags = new ConcurrentHashMap<List,AspectList>();
/**
* Checks to see if the passed item/block already has aspects associated with it.
* @param id
* @param meta
* @return
*/
public static boolean exists(int id, int meta) {
AspectList tmp = ThaumcraftApi.objectTags.get(Arrays.asList(id,meta));
if (tmp==null) {
tmp = ThaumcraftApi.objectTags.get(Arrays.asList(id,-1));
if (meta==-1 && tmp==null) {
int index=0;
do {
tmp = ThaumcraftApi.objectTags.get(Arrays.asList(id,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:<p>
* <i>ThaumcraftApi.registerObjectTag(Block.cobblestone.blockID, -1, (new AspectList()).add(Aspect.ENTROPY, 1).add(Aspect.STONE, 1));</i>
* @param id
* @param meta pass -1 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(int id, int meta, AspectList aspects) {
if (aspects==null) aspects=new AspectList();
objectTags.put(Arrays.asList(id,meta), aspects);
}
/**
* Used to assign apsects to the given item/block. Here is an example of the declaration for cobblestone:<p>
* <i>ThaumcraftApi.registerObjectTag(Block.cobblestone.blockID, new int[]{0,1}, (new AspectList()).add(Aspect.ENTROPY, 1).add(Aspect.STONE, 1));</i>
* @param id
* @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(int id, int[] meta, AspectList aspects) {
if (aspects==null) aspects=new AspectList();
objectTags.put(Arrays.asList(id,meta), aspects);
}
/**
* 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<ItemStack> ores = OreDictionary.getOres(oreDict);
if (ores!=null && ores.size()>0) {
for (ItemStack ore:ores) {
int d = ore.getItemDamage();
if (d==OreDictionary.WILDCARD_VALUE) d = -1;
objectTags.put(Arrays.asList(ore.itemID, d), aspects);
}
}
}
/**
* 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:<p>
* <i>ThaumcraftApi.registerComplexObjectTag(Block.pistonBase.blockID, 0, (new AspectList()).add(Aspect.MECHANISM, 2).add(Aspect.MOTION, 4));</i>
* @param id
* @param meta pass -1 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(int id, int meta, AspectList aspects ) {
if (!exists(id,meta)) {
AspectList tmp = ThaumcraftApiHelper.generateTags(id, meta);
if (tmp != null && tmp.size()>0) {
for(Aspect tag:tmp.getAspects()) {
aspects.add(tag, tmp.getAmount(tag));
}
}
registerObjectTag(id,meta,aspects);
} else {
AspectList tmp = ThaumcraftApiHelper.getObjectAspects(new ItemStack(id,1,meta));
for(Aspect tag:aspects.getAspects()) {
tmp.merge(tag, tmp.getAmount(tag));
}
registerObjectTag(id,meta,tmp);
}
}
//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 -1 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 -1 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 -1 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 (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" string message.
* Sending a metadata of -1 will mean the metadata won't get checked.
* Example for vanilla wheat:
* FMLInterModComms.sendMessage("Thaumcraft", "lampBlacklist", new ItemStack(Block.crops,1,-1));
*/
}