diff --git a/build.gradle b/build.gradle
index d68ff7afa..16e822129 100644
--- a/build.gradle
+++ b/build.gradle
@@ -16,6 +16,7 @@ buildscript {
plugins {
id 'com.matthewprenger.cursegradle' version "${cursegradle_version}"
+// id 'com.blamejared.createtweaker.default'
}
apply plugin: 'net.minecraftforge.gradle'
apply plugin: 'org.parchmentmc.librarian.forgegradle'
@@ -115,6 +116,15 @@ repositories {
url = "https://maven.jaackson.me"
}
+ maven {
+ url = 'https://maven.blamejared.com'
+ name = 'BlameJared Maven'
+ }
+
+ maven {
+ url = 'https://www.jitpack.io'
+ }
+
exclusiveContent {
forRepository {
maven {
@@ -176,9 +186,14 @@ dependencies {
compileOnly fg.deobf("com.teamabnormals:blueprint:${minecraft_version}-${blueprint_version}")
implementation fg.deobf("com.teamabnormals:blueprint:${minecraft_version}-${blueprint_version}")
-
+ // CraftTweaker
+ var tweaker = "com.blamejared.crafttweaker:CraftTweaker-forge-${minecraft_version}:${crafttweaker_version}"
+ implementation fg.deobf(tweaker)
+ annotationProcessor(tweaker)
+ compileOnly("com.blamejared.crafttweaker:CraftTweaker-common-${minecraft_version}:${crafttweaker_version}")
// ANNOTATION PROCESSORS
-
+// implementation fg.deobf("com.blamejared:CreateTweaker:1.0.0.12")
+ annotationProcessor("com.blamejared.crafttweaker:Crafttweaker_Annotation_Processors:3.0.0.15")
annotationProcessor "org.spongepowered:mixin:${mixin_version}:processor"
}
@@ -197,6 +212,10 @@ jar {
}
}
+javadoc {
+ options.encoding = 'UTF-8'
+}
+
// Example configuration to allow publishing using the maven-publish plugin
// This is the preferred method to reobfuscate your jar file
jar.finalizedBy('reobfJar')
diff --git a/docsOut/docs.json b/docsOut/docs.json
new file mode 100644
index 000000000..ebf5e305d
--- /dev/null
+++ b/docsOut/docs.json
@@ -0,0 +1 @@
+{"nav":{"Mods":{"Destroy":{"AgingManager":"mods/destroy/AgingManager.md","Atom":"mods/destroy/Atom.md","CentrifugationManager":"mods/destroy/CentrifugationManager.md","ChargingManager":"mods/destroy/ChargingManager.md","DistillationManager":"mods/destroy/DistillationManager.md","ElectrolysisManager":"mods/destroy/ElectrolysisManager.md","Element":"mods/destroy/Element.md","Formula":"mods/destroy/Formula.md","IDestroyRecipeManager":"mods/destroy/IDestroyRecipeManager.md","Mixture":"mods/destroy/Mixture.md","Molecule":"mods/destroy/Molecule.md","MoleculeBuilder":"mods/destroy/MoleculeBuilder.md","Molecules":"mods/destroy/Molecules.md","Reaction":"mods/destroy/Reaction.md","ReactionBuilder":"mods/destroy/ReactionBuilder.md","Reactions":"mods/destroy/Reactions.md"}}}}
\ No newline at end of file
diff --git a/docsOut/docs/mods/destroy/AgingManager.json b/docsOut/docs/mods/destroy/AgingManager.json
new file mode 100644
index 000000000..8942b6446
--- /dev/null
+++ b/docsOut/docs/mods/destroy/AgingManager.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/AgingManager.md","zenCodeName":"mods.destroy.AgingManager","searchTerms":["getRecipeByName","addJsonRecipe","allRecipes","removeByModid","remove","recipeMap","addRecipe","removeAll","removeByName","getRecipeMap","getAllRecipes","registerRecipe","removeByInput","mods.destroy.IDestroyRecipeManager","getRecipesByOutput","removeByRegex"]}
diff --git a/docsOut/docs/mods/destroy/AgingManager.md b/docsOut/docs/mods/destroy/AgingManager.md
new file mode 100644
index 000000000..75fd97c30
--- /dev/null
+++ b/docsOut/docs/mods/destroy/AgingManager.md
@@ -0,0 +1,207 @@
+# AgingManager
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.AgingManager;
+```
+
+
+## Implemented Interfaces
+AgingManager implements the following interfaces. That means all methods defined in these interfaces are also available in AgingManager
+
+- [IDestroyRecipeManager](/mods/destroy/IDestroyRecipeManager)
+
+## Methods
+
+:::group{name=addJsonRecipe}
+
+```zenscript
+AgingManager.addJsonRecipe(name as string, mapData as MapData)
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------|
+| name | string |
+| mapData | [MapData](/vanilla/api/data/MapData) |
+
+
+:::
+
+:::group{name=addRecipe}
+
+Adds an aging recipe to the aging barrel
+
+```zenscript
+// AgingManager.addRecipe(name as string, input as FluidIngredient, items as IIngredient[], result as IFluidStack, processingTime as int)
+
+myAgingManager.addRecipe("wine_aging", , [, ], per 1 bucket of input, 1200);
+```
+
+| Parameter | Type | Description | Optional | Default Value |
+|----------------|------------------------------------------------------|----------------------------------------|----------|---------------|
+| name | string | Name of the recipe | false | |
+| input | [FluidIngredient](/forge/api/fluid/FluidIngredient) | Input fluid of the recipe | false | |
+| items | [IIngredient](/vanilla/api/ingredient/IIngredient)[] | Additional items to the aging process | false | |
+| result | [IFluidStack](/vanilla/api/fluid/IFluidStack) | Resulting fluid | false | |
+| processingTime | int | Processing time of the recipe in ticks | true | 1200 |
+
+
+:::
+
+:::group{name=getAllRecipes}
+
+Return Type: stdlib.List<T>
+
+```zenscript
+// AgingManager.getAllRecipes() as stdlib.List
+
+myAgingManager.getAllRecipes();
+```
+
+:::
+
+:::group{name=getRecipeByName}
+
+Return Type: T
+
+```zenscript
+AgingManager.getRecipeByName(name as string) as T
+```
+
+| Parameter | Type |
+|-----------|--------|
+| name | string |
+
+
+:::
+
+:::group{name=getRecipeMap}
+
+Return Type: T[[ResourceLocation](/vanilla/api/resource/ResourceLocation)]
+
+```zenscript
+// AgingManager.getRecipeMap() as T[ResourceLocation]
+
+myAgingManager.getRecipeMap();
+```
+
+:::
+
+:::group{name=getRecipesByOutput}
+
+Return Type: stdlib.List<T>
+
+```zenscript
+AgingManager.getRecipesByOutput(output as IIngredient) as stdlib.List
+```
+
+| Parameter | Type |
+|-----------|----------------------------------------------------|
+| output | [IIngredient](/vanilla/api/ingredient/IIngredient) |
+
+
+:::
+
+:::group{name=registerRecipe}
+
+Registers a recipe using a builder approach.
+
+```zenscript
+AgingManager.registerRecipe(name as string, recipeBuilder as Consumer)
+```
+
+| Parameter | Type | Description |
+|---------------|----------|-------------------------|
+| name | string | The name of the recipe. |
+| recipeBuilder | Consumer | The recipe builder. |
+
+
+:::
+
+:::group{name=remove}
+
+```zenscript
+AgingManager.remove(output as IIngredient)
+```
+
+| Parameter | Type |
+|-----------|----------------------------------------------------|
+| output | [IIngredient](/vanilla/api/ingredient/IIngredient) |
+
+
+:::
+
+:::group{name=removeAll}
+
+```zenscript
+// AgingManager.removeAll()
+
+myAgingManager.removeAll();
+```
+
+:::
+
+:::group{name=removeByInput}
+
+```zenscript
+AgingManager.removeByInput(input as IItemStack)
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------------|
+| input | [IItemStack](/vanilla/api/item/IItemStack) |
+
+
+:::
+
+:::group{name=removeByModid}
+
+```zenscript
+AgingManager.removeByModid(modid as string, exclude as Predicate)
+```
+
+| Parameter | Type | Optional | Default Value |
+|-----------|-------------------------|----------|-----------------------------------|
+| modid | string | false | |
+| exclude | Predicate<string> | true | (name as string) as bool => false |
+
+
+:::
+
+:::group{name=removeByName}
+
+```zenscript
+AgingManager.removeByName(names as string[])
+```
+
+| Parameter | Type |
+|-----------|----------|
+| names | string[] |
+
+
+:::
+
+:::group{name=removeByRegex}
+
+```zenscript
+AgingManager.removeByRegex(regex as string, exclude as Predicate)
+```
+
+| Parameter | Type | Optional | Default Value |
+|-----------|-------------------------|----------|-----------------------------------|
+| regex | string | false | |
+| exclude | Predicate<string> | true | (name as string) as bool => false |
+
+
+:::
+
+
+## Properties
+
+| Name | Type | Has Getter | Has Setter |
+|------------|---------------------------------------------------------------|------------|------------|
+| allRecipes | stdlib.List<T> | true | false |
+| recipeMap | T[[ResourceLocation](/vanilla/api/resource/ResourceLocation)] | true | false |
+
diff --git a/docsOut/docs/mods/destroy/Atom.json b/docsOut/docs/mods/destroy/Atom.json
new file mode 100644
index 000000000..0f5151eab
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Atom.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/Atom.md","zenCodeName":"mods.destroy.Atom","searchTerms":["getPartial","getElement","isHydrogen"]}
diff --git a/docsOut/docs/mods/destroy/Atom.md b/docsOut/docs/mods/destroy/Atom.md
new file mode 100644
index 000000000..88378a201
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Atom.md
@@ -0,0 +1,49 @@
+# Atom
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.Atom;
+```
+
+
+## Methods
+
+:::group{name=getElement}
+
+Return Type: [Element](/mods/destroy/Element)
+
+```zenscript
+// Atom.getElement() as Element
+
+myAtom.getElement();
+```
+
+:::
+
+:::group{name=getPartial}
+
+Return Type: **invalid**
+
+```zenscript
+// Atom.getPartial() as invalid
+
+myAtom.getPartial();
+```
+
+:::
+
+:::group{name=isHydrogen}
+
+Return Type: boolean
+
+```zenscript
+// Atom.isHydrogen() as boolean
+
+myAtom.isHydrogen();
+```
+
+:::
+
+
diff --git a/docsOut/docs/mods/destroy/CentrifugationManager.json b/docsOut/docs/mods/destroy/CentrifugationManager.json
new file mode 100644
index 000000000..53c0595ae
--- /dev/null
+++ b/docsOut/docs/mods/destroy/CentrifugationManager.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/CentrifugationManager.md","zenCodeName":"mods.destroy.CentrifugationManager","searchTerms":["getRecipeByName","addJsonRecipe","allRecipes","removeByModid","remove","recipeMap","addRecipe","removeAll","removeByName","getRecipeMap","getAllRecipes","registerRecipe","removeByInput","mods.destroy.IDestroyRecipeManager","getRecipesByOutput","removeByRegex"]}
diff --git a/docsOut/docs/mods/destroy/CentrifugationManager.md b/docsOut/docs/mods/destroy/CentrifugationManager.md
new file mode 100644
index 000000000..91b98d705
--- /dev/null
+++ b/docsOut/docs/mods/destroy/CentrifugationManager.md
@@ -0,0 +1,204 @@
+# CentrifugationManager
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.CentrifugationManager;
+```
+
+
+## Implemented Interfaces
+CentrifugationManager implements the following interfaces. That means all methods defined in these interfaces are also available in CentrifugationManager
+
+- [IDestroyRecipeManager](/mods/destroy/IDestroyRecipeManager)
+
+## Methods
+
+:::group{name=addJsonRecipe}
+
+```zenscript
+CentrifugationManager.addJsonRecipe(name as string, mapData as MapData)
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------|
+| name | string |
+| mapData | [MapData](/vanilla/api/data/MapData) |
+
+
+:::
+
+:::group{name=addRecipe}
+
+Adds recipe to the centrifuge
+
+```zenscript
+CentrifugationManager.addRecipe(name as string, input as FluidIngredient, output as IFluidStack[], processingTime as int)
+```
+
+| Parameter | Type | Description | Optional | Default Value |
+|----------------|-----------------------------------------------------|-----------------------------------------|----------|---------------|
+| name | string | Name of the recipe | false | |
+| input | [FluidIngredient](/forge/api/fluid/FluidIngredient) | The input fluid | false | |
+| output | [IFluidStack](/vanilla/api/fluid/IFluidStack)[] | 2 output fluids per 1 mB of input | false | |
+| processingTime | int | Processing time of this recipe in ticks | true | 1200 |
+
+
+:::
+
+:::group{name=getAllRecipes}
+
+Return Type: stdlib.List<T>
+
+```zenscript
+// CentrifugationManager.getAllRecipes() as stdlib.List
+
+myCentrifugationManager.getAllRecipes();
+```
+
+:::
+
+:::group{name=getRecipeByName}
+
+Return Type: T
+
+```zenscript
+CentrifugationManager.getRecipeByName(name as string) as T
+```
+
+| Parameter | Type |
+|-----------|--------|
+| name | string |
+
+
+:::
+
+:::group{name=getRecipeMap}
+
+Return Type: T[[ResourceLocation](/vanilla/api/resource/ResourceLocation)]
+
+```zenscript
+// CentrifugationManager.getRecipeMap() as T[ResourceLocation]
+
+myCentrifugationManager.getRecipeMap();
+```
+
+:::
+
+:::group{name=getRecipesByOutput}
+
+Return Type: stdlib.List<T>
+
+```zenscript
+CentrifugationManager.getRecipesByOutput(output as IIngredient) as stdlib.List
+```
+
+| Parameter | Type |
+|-----------|----------------------------------------------------|
+| output | [IIngredient](/vanilla/api/ingredient/IIngredient) |
+
+
+:::
+
+:::group{name=registerRecipe}
+
+Registers a recipe using a builder approach.
+
+```zenscript
+CentrifugationManager.registerRecipe(name as string, recipeBuilder as Consumer)
+```
+
+| Parameter | Type | Description |
+|---------------|----------|-------------------------|
+| name | string | The name of the recipe. |
+| recipeBuilder | Consumer | The recipe builder. |
+
+
+:::
+
+:::group{name=remove}
+
+```zenscript
+CentrifugationManager.remove(output as IIngredient)
+```
+
+| Parameter | Type |
+|-----------|----------------------------------------------------|
+| output | [IIngredient](/vanilla/api/ingredient/IIngredient) |
+
+
+:::
+
+:::group{name=removeAll}
+
+```zenscript
+// CentrifugationManager.removeAll()
+
+myCentrifugationManager.removeAll();
+```
+
+:::
+
+:::group{name=removeByInput}
+
+```zenscript
+CentrifugationManager.removeByInput(input as IItemStack)
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------------|
+| input | [IItemStack](/vanilla/api/item/IItemStack) |
+
+
+:::
+
+:::group{name=removeByModid}
+
+```zenscript
+CentrifugationManager.removeByModid(modid as string, exclude as Predicate)
+```
+
+| Parameter | Type | Optional | Default Value |
+|-----------|-------------------------|----------|-----------------------------------|
+| modid | string | false | |
+| exclude | Predicate<string> | true | (name as string) as bool => false |
+
+
+:::
+
+:::group{name=removeByName}
+
+```zenscript
+CentrifugationManager.removeByName(names as string[])
+```
+
+| Parameter | Type |
+|-----------|----------|
+| names | string[] |
+
+
+:::
+
+:::group{name=removeByRegex}
+
+```zenscript
+CentrifugationManager.removeByRegex(regex as string, exclude as Predicate)
+```
+
+| Parameter | Type | Optional | Default Value |
+|-----------|-------------------------|----------|-----------------------------------|
+| regex | string | false | |
+| exclude | Predicate<string> | true | (name as string) as bool => false |
+
+
+:::
+
+
+## Properties
+
+| Name | Type | Has Getter | Has Setter |
+|------------|---------------------------------------------------------------|------------|------------|
+| allRecipes | stdlib.List<T> | true | false |
+| recipeMap | T[[ResourceLocation](/vanilla/api/resource/ResourceLocation)] | true | false |
+
diff --git a/docsOut/docs/mods/destroy/ChargingManager.json b/docsOut/docs/mods/destroy/ChargingManager.json
new file mode 100644
index 000000000..d80fa034b
--- /dev/null
+++ b/docsOut/docs/mods/destroy/ChargingManager.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/ChargingManager.md","zenCodeName":"mods.destroy.ChargingManager","searchTerms":["getRecipeByName","addJsonRecipe","allRecipes","removeByModid","remove","recipeMap","addRecipe","removeAll","removeByName","getRecipeMap","getAllRecipes","registerRecipe","removeByInput","mods.destroy.IDestroyRecipeManager","getRecipesByOutput","removeByRegex"]}
diff --git a/docsOut/docs/mods/destroy/ChargingManager.md b/docsOut/docs/mods/destroy/ChargingManager.md
new file mode 100644
index 000000000..cff31fb50
--- /dev/null
+++ b/docsOut/docs/mods/destroy/ChargingManager.md
@@ -0,0 +1,205 @@
+# ChargingManager
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.ChargingManager;
+```
+
+
+## Implemented Interfaces
+ChargingManager implements the following interfaces. That means all methods defined in these interfaces are also available in ChargingManager
+
+- [IDestroyRecipeManager](/mods/destroy/IDestroyRecipeManager)
+
+## Methods
+
+:::group{name=addJsonRecipe}
+
+```zenscript
+ChargingManager.addJsonRecipe(name as string, mapData as MapData)
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------|
+| name | string |
+| mapData | [MapData](/vanilla/api/data/MapData) |
+
+
+:::
+
+:::group{name=addRecipe}
+
+Adds a recipe to the dynamo machine. (This feature is likely to get removed or heavily modified in future release of Destroy)
+
+```zenscript
+// ChargingManager.addRecipe(name as string, input as IIngredient, output as IItemStack)
+
+myChargingManager.addRecipe("charge_iron", , );
+```
+
+| Parameter | Type | Description |
+|-----------|----------------------------------------------------|--------------------|
+| name | string | Name of the recipe |
+| input | [IIngredient](/vanilla/api/ingredient/IIngredient) | Item to charge |
+| output | [IItemStack](/vanilla/api/item/IItemStack) | Output item |
+
+
+:::
+
+:::group{name=getAllRecipes}
+
+Return Type: stdlib.List<T>
+
+```zenscript
+// ChargingManager.getAllRecipes() as stdlib.List
+
+myChargingManager.getAllRecipes();
+```
+
+:::
+
+:::group{name=getRecipeByName}
+
+Return Type: T
+
+```zenscript
+ChargingManager.getRecipeByName(name as string) as T
+```
+
+| Parameter | Type |
+|-----------|--------|
+| name | string |
+
+
+:::
+
+:::group{name=getRecipeMap}
+
+Return Type: T[[ResourceLocation](/vanilla/api/resource/ResourceLocation)]
+
+```zenscript
+// ChargingManager.getRecipeMap() as T[ResourceLocation]
+
+myChargingManager.getRecipeMap();
+```
+
+:::
+
+:::group{name=getRecipesByOutput}
+
+Return Type: stdlib.List<T>
+
+```zenscript
+ChargingManager.getRecipesByOutput(output as IIngredient) as stdlib.List
+```
+
+| Parameter | Type |
+|-----------|----------------------------------------------------|
+| output | [IIngredient](/vanilla/api/ingredient/IIngredient) |
+
+
+:::
+
+:::group{name=registerRecipe}
+
+Registers a recipe using a builder approach.
+
+```zenscript
+ChargingManager.registerRecipe(name as string, recipeBuilder as Consumer)
+```
+
+| Parameter | Type | Description |
+|---------------|----------|-------------------------|
+| name | string | The name of the recipe. |
+| recipeBuilder | Consumer | The recipe builder. |
+
+
+:::
+
+:::group{name=remove}
+
+```zenscript
+ChargingManager.remove(output as IIngredient)
+```
+
+| Parameter | Type |
+|-----------|----------------------------------------------------|
+| output | [IIngredient](/vanilla/api/ingredient/IIngredient) |
+
+
+:::
+
+:::group{name=removeAll}
+
+```zenscript
+// ChargingManager.removeAll()
+
+myChargingManager.removeAll();
+```
+
+:::
+
+:::group{name=removeByInput}
+
+```zenscript
+ChargingManager.removeByInput(input as IItemStack)
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------------|
+| input | [IItemStack](/vanilla/api/item/IItemStack) |
+
+
+:::
+
+:::group{name=removeByModid}
+
+```zenscript
+ChargingManager.removeByModid(modid as string, exclude as Predicate)
+```
+
+| Parameter | Type | Optional | Default Value |
+|-----------|-------------------------|----------|-----------------------------------|
+| modid | string | false | |
+| exclude | Predicate<string> | true | (name as string) as bool => false |
+
+
+:::
+
+:::group{name=removeByName}
+
+```zenscript
+ChargingManager.removeByName(names as string[])
+```
+
+| Parameter | Type |
+|-----------|----------|
+| names | string[] |
+
+
+:::
+
+:::group{name=removeByRegex}
+
+```zenscript
+ChargingManager.removeByRegex(regex as string, exclude as Predicate)
+```
+
+| Parameter | Type | Optional | Default Value |
+|-----------|-------------------------|----------|-----------------------------------|
+| regex | string | false | |
+| exclude | Predicate<string> | true | (name as string) as bool => false |
+
+
+:::
+
+
+## Properties
+
+| Name | Type | Has Getter | Has Setter |
+|------------|---------------------------------------------------------------|------------|------------|
+| allRecipes | stdlib.List<T> | true | false |
+| recipeMap | T[[ResourceLocation](/vanilla/api/resource/ResourceLocation)] | true | false |
+
diff --git a/docsOut/docs/mods/destroy/DistillationManager.json b/docsOut/docs/mods/destroy/DistillationManager.json
new file mode 100644
index 000000000..9d7a65abe
--- /dev/null
+++ b/docsOut/docs/mods/destroy/DistillationManager.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/DistillationManager.md","zenCodeName":"mods.destroy.DistillationManager","searchTerms":["getRecipeByName","addJsonRecipe","allRecipes","removeByModid","remove","recipeMap","addRecipe","removeAll","removeByName","getRecipeMap","getAllRecipes","registerRecipe","removeByInput","mods.destroy.IDestroyRecipeManager","getRecipesByOutput","removeByRegex"]}
diff --git a/docsOut/docs/mods/destroy/DistillationManager.md b/docsOut/docs/mods/destroy/DistillationManager.md
new file mode 100644
index 000000000..7d8f8fefe
--- /dev/null
+++ b/docsOut/docs/mods/destroy/DistillationManager.md
@@ -0,0 +1,204 @@
+# DistillationManager
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.DistillationManager;
+```
+
+
+## Implemented Interfaces
+DistillationManager implements the following interfaces. That means all methods defined in these interfaces are also available in DistillationManager
+
+- [IDestroyRecipeManager](/mods/destroy/IDestroyRecipeManager)
+
+## Methods
+
+:::group{name=addJsonRecipe}
+
+```zenscript
+DistillationManager.addJsonRecipe(name as string, mapData as MapData)
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------|
+| name | string |
+| mapData | [MapData](/vanilla/api/data/MapData) |
+
+
+:::
+
+:::group{name=addRecipe}
+
+Adds recipe to the distillation tower.
+
+```zenscript
+DistillationManager.addRecipe(name as string, heat as invalid, input as FluidIngredient, fractions as IFluidStack[])
+```
+
+| Parameter | Type | Description |
+|-----------|-----------------------------------------------------|--------------------------------------------------------------|
+| name | string | Name of the recipe |
+| heat | **invalid** | Heat required |
+| input | [FluidIngredient](/forge/api/fluid/FluidIngredient) | Input fluid, can't be a mixture |
+| fractions | [IFluidStack](/vanilla/api/fluid/IFluidStack)[] | How much of other fluids should be created per 1 mB of input |
+
+
+:::
+
+:::group{name=getAllRecipes}
+
+Return Type: stdlib.List<T>
+
+```zenscript
+// DistillationManager.getAllRecipes() as stdlib.List
+
+myDistillationManager.getAllRecipes();
+```
+
+:::
+
+:::group{name=getRecipeByName}
+
+Return Type: T
+
+```zenscript
+DistillationManager.getRecipeByName(name as string) as T
+```
+
+| Parameter | Type |
+|-----------|--------|
+| name | string |
+
+
+:::
+
+:::group{name=getRecipeMap}
+
+Return Type: T[[ResourceLocation](/vanilla/api/resource/ResourceLocation)]
+
+```zenscript
+// DistillationManager.getRecipeMap() as T[ResourceLocation]
+
+myDistillationManager.getRecipeMap();
+```
+
+:::
+
+:::group{name=getRecipesByOutput}
+
+Return Type: stdlib.List<T>
+
+```zenscript
+DistillationManager.getRecipesByOutput(output as IIngredient) as stdlib.List
+```
+
+| Parameter | Type |
+|-----------|----------------------------------------------------|
+| output | [IIngredient](/vanilla/api/ingredient/IIngredient) |
+
+
+:::
+
+:::group{name=registerRecipe}
+
+Registers a recipe using a builder approach.
+
+```zenscript
+DistillationManager.registerRecipe(name as string, recipeBuilder as Consumer)
+```
+
+| Parameter | Type | Description |
+|---------------|----------|-------------------------|
+| name | string | The name of the recipe. |
+| recipeBuilder | Consumer | The recipe builder. |
+
+
+:::
+
+:::group{name=remove}
+
+```zenscript
+DistillationManager.remove(output as IIngredient)
+```
+
+| Parameter | Type |
+|-----------|----------------------------------------------------|
+| output | [IIngredient](/vanilla/api/ingredient/IIngredient) |
+
+
+:::
+
+:::group{name=removeAll}
+
+```zenscript
+// DistillationManager.removeAll()
+
+myDistillationManager.removeAll();
+```
+
+:::
+
+:::group{name=removeByInput}
+
+```zenscript
+DistillationManager.removeByInput(input as IItemStack)
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------------|
+| input | [IItemStack](/vanilla/api/item/IItemStack) |
+
+
+:::
+
+:::group{name=removeByModid}
+
+```zenscript
+DistillationManager.removeByModid(modid as string, exclude as Predicate)
+```
+
+| Parameter | Type | Optional | Default Value |
+|-----------|-------------------------|----------|-----------------------------------|
+| modid | string | false | |
+| exclude | Predicate<string> | true | (name as string) as bool => false |
+
+
+:::
+
+:::group{name=removeByName}
+
+```zenscript
+DistillationManager.removeByName(names as string[])
+```
+
+| Parameter | Type |
+|-----------|----------|
+| names | string[] |
+
+
+:::
+
+:::group{name=removeByRegex}
+
+```zenscript
+DistillationManager.removeByRegex(regex as string, exclude as Predicate)
+```
+
+| Parameter | Type | Optional | Default Value |
+|-----------|-------------------------|----------|-----------------------------------|
+| regex | string | false | |
+| exclude | Predicate<string> | true | (name as string) as bool => false |
+
+
+:::
+
+
+## Properties
+
+| Name | Type | Has Getter | Has Setter |
+|------------|---------------------------------------------------------------|------------|------------|
+| allRecipes | stdlib.List<T> | true | false |
+| recipeMap | T[[ResourceLocation](/vanilla/api/resource/ResourceLocation)] | true | false |
+
diff --git a/docsOut/docs/mods/destroy/ElectrolysisManager.json b/docsOut/docs/mods/destroy/ElectrolysisManager.json
new file mode 100644
index 000000000..4d0d44ec9
--- /dev/null
+++ b/docsOut/docs/mods/destroy/ElectrolysisManager.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/ElectrolysisManager.md","zenCodeName":"mods.destroy.ElectrolysisManager","searchTerms":["getRecipeByName","addJsonRecipe","allRecipes","removeByModid","remove","recipeMap","addRecipe","removeAll","removeByName","getRecipeMap","getAllRecipes","registerRecipe","removeByInput","mods.destroy.IDestroyRecipeManager","getRecipesByOutput","removeByRegex"]}
diff --git a/docsOut/docs/mods/destroy/ElectrolysisManager.md b/docsOut/docs/mods/destroy/ElectrolysisManager.md
new file mode 100644
index 000000000..53c8c7aa5
--- /dev/null
+++ b/docsOut/docs/mods/destroy/ElectrolysisManager.md
@@ -0,0 +1,230 @@
+# ElectrolysisManager
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.ElectrolysisManager;
+```
+
+
+## Implemented Interfaces
+ElectrolysisManager implements the following interfaces. That means all methods defined in these interfaces are also available in ElectrolysisManager
+
+- [IDestroyRecipeManager](/mods/destroy/IDestroyRecipeManager)
+
+## Methods
+
+:::group{name=addJsonRecipe}
+
+```zenscript
+ElectrolysisManager.addJsonRecipe(name as string, mapData as MapData)
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------|
+| name | string |
+| mapData | [MapData](/vanilla/api/data/MapData) |
+
+
+:::
+
+:::group{name=addRecipe}
+
+Adds an electrolysis recipe that outputs FluidStacks.
+
+```zenscript
+// ElectrolysisManager.addRecipe(name as string, heat as invalid, outputs as IFluidStack[], itemInputs as IIngredientWithAmount[], fluidInputs as FluidIngredient[], duration as int)
+
+myElectrolysisManager.addRecipe("fluid_electrolysed", , [ * 200], [ * 2], [ * 250], 200);
+```
+
+| Parameter | Type | Description | Optional | Default Value |
+|-------------|--------------------------------------------------------------------------|------------------------------------------|----------|------------------------------------------------|
+| name | string | The name of the recipe. | false | |
+| heat | **invalid** | The required heat of the recipe. | false | |
+| outputs | [IFluidStack](/vanilla/api/fluid/IFluidStack)[] | The output FluidStacks of the recipe. | false | |
+| itemInputs | [IIngredientWithAmount](/vanilla/api/ingredient/IIngredientWithAmount)[] | The item inputs of the recipe. | false | |
+| fluidInputs | [FluidIngredient](/forge/api/fluid/FluidIngredient)[] | The optional fluid inputs of the recipe. | true | [] as crafttweaker.api.fluid.FluidIngredient[] |
+| duration | int | The duration of the recipe in ticks. | true | 100 |
+
+
+:::
+
+:::group{name=addRecipe}
+
+Adds an Electrolysis recipe that outputs ItemStacks.
+
+```zenscript
+// ElectrolysisManager.addRecipe(name as string, heat as invalid, outputs as Percentaged[], itemInputs as IIngredientWithAmount[], fluidInputs as FluidIngredient[], duration as int)
+
+myElectrolysisManager.addRecipe("electrolysed", , [ % 50, , ( * 2) % 12], [ * 2], [ * 250], 200);
+```
+
+| Parameter | Type | Description | Optional | Default Value |
+|-------------|---------------------------------------------------------------------------------------------------------|------------------------------------------|----------|------------------------------------------------|
+| name | string | The name of the recipe. | false | |
+| heat | **invalid** | The required heat of the recipe. | false | |
+| outputs | [Percentaged](/vanilla/api/util/random/Percentaged)<[IItemStack](/vanilla/api/item/IItemStack)>[] | The output ItemStacks of the recipe. | false | |
+| itemInputs | [IIngredientWithAmount](/vanilla/api/ingredient/IIngredientWithAmount)[] | The item inputs of the recipe. | false | |
+| fluidInputs | [FluidIngredient](/forge/api/fluid/FluidIngredient)[] | The optional fluid inputs of the recipe. | true | [] as crafttweaker.api.fluid.FluidIngredient[] |
+| duration | int | The duration of the recipe in ticks. | true | 100 |
+
+
+:::
+
+:::group{name=getAllRecipes}
+
+Return Type: stdlib.List<T>
+
+```zenscript
+// ElectrolysisManager.getAllRecipes() as stdlib.List
+
+myElectrolysisManager.getAllRecipes();
+```
+
+:::
+
+:::group{name=getRecipeByName}
+
+Return Type: T
+
+```zenscript
+ElectrolysisManager.getRecipeByName(name as string) as T
+```
+
+| Parameter | Type |
+|-----------|--------|
+| name | string |
+
+
+:::
+
+:::group{name=getRecipeMap}
+
+Return Type: T[[ResourceLocation](/vanilla/api/resource/ResourceLocation)]
+
+```zenscript
+// ElectrolysisManager.getRecipeMap() as T[ResourceLocation]
+
+myElectrolysisManager.getRecipeMap();
+```
+
+:::
+
+:::group{name=getRecipesByOutput}
+
+Return Type: stdlib.List<T>
+
+```zenscript
+ElectrolysisManager.getRecipesByOutput(output as IIngredient) as stdlib.List
+```
+
+| Parameter | Type |
+|-----------|----------------------------------------------------|
+| output | [IIngredient](/vanilla/api/ingredient/IIngredient) |
+
+
+:::
+
+:::group{name=registerRecipe}
+
+Registers a recipe using a builder approach.
+
+```zenscript
+ElectrolysisManager.registerRecipe(name as string, recipeBuilder as Consumer)
+```
+
+| Parameter | Type | Description |
+|---------------|----------|-------------------------|
+| name | string | The name of the recipe. |
+| recipeBuilder | Consumer | The recipe builder. |
+
+
+:::
+
+:::group{name=remove}
+
+```zenscript
+ElectrolysisManager.remove(output as IIngredient)
+```
+
+| Parameter | Type |
+|-----------|----------------------------------------------------|
+| output | [IIngredient](/vanilla/api/ingredient/IIngredient) |
+
+
+:::
+
+:::group{name=removeAll}
+
+```zenscript
+// ElectrolysisManager.removeAll()
+
+myElectrolysisManager.removeAll();
+```
+
+:::
+
+:::group{name=removeByInput}
+
+```zenscript
+ElectrolysisManager.removeByInput(input as IItemStack)
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------------|
+| input | [IItemStack](/vanilla/api/item/IItemStack) |
+
+
+:::
+
+:::group{name=removeByModid}
+
+```zenscript
+ElectrolysisManager.removeByModid(modid as string, exclude as Predicate)
+```
+
+| Parameter | Type | Optional | Default Value |
+|-----------|-------------------------|----------|-----------------------------------|
+| modid | string | false | |
+| exclude | Predicate<string> | true | (name as string) as bool => false |
+
+
+:::
+
+:::group{name=removeByName}
+
+```zenscript
+ElectrolysisManager.removeByName(names as string[])
+```
+
+| Parameter | Type |
+|-----------|----------|
+| names | string[] |
+
+
+:::
+
+:::group{name=removeByRegex}
+
+```zenscript
+ElectrolysisManager.removeByRegex(regex as string, exclude as Predicate)
+```
+
+| Parameter | Type | Optional | Default Value |
+|-----------|-------------------------|----------|-----------------------------------|
+| regex | string | false | |
+| exclude | Predicate<string> | true | (name as string) as bool => false |
+
+
+:::
+
+
+## Properties
+
+| Name | Type | Has Getter | Has Setter |
+|------------|---------------------------------------------------------------|------------|------------|
+| allRecipes | stdlib.List<T> | true | false |
+| recipeMap | T[[ResourceLocation](/vanilla/api/resource/ResourceLocation)] | true | false |
+
diff --git a/docsOut/docs/mods/destroy/Element.json b/docsOut/docs/mods/destroy/Element.json
new file mode 100644
index 000000000..aab8cc726
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Element.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/Element.md","zenCodeName":"mods.destroy.Element","searchTerms":["GOLD","getPartial","setPartial","getElectronegativity","isValidValency","PLATINUM","CHLORINE","SULFUR","COPPER","getGeometry","ZINC","MERCURY","ZIRCONIUM","CALCIUM","getNextLowestValency","R_GROUP","POTASSIUM","SODIUM","CARBON","getMass","IODINE","IRON","OXYGEN","HYDROGEN","getMaxValency","NICKEL","NITROGEN","FLUORINE","getSymbol"]}
diff --git a/docsOut/docs/mods/destroy/Element.md b/docsOut/docs/mods/destroy/Element.md
new file mode 100644
index 000000000..692925f93
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Element.md
@@ -0,0 +1,157 @@
+# Element
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.Element;
+```
+
+
+## Enum Constants
+
+Element is an enum. It has 20 enum constants. They are accessible using the code below.
+
+```zenscript
+Element.R_GROUP
+Element.CARBON
+Element.HYDROGEN
+Element.SULFUR
+Element.NITROGEN
+Element.OXYGEN
+Element.FLUORINE
+Element.SODIUM
+Element.CHLORINE
+Element.POTASSIUM
+Element.CALCIUM
+Element.IRON
+Element.NICKEL
+Element.COPPER
+Element.ZINC
+Element.ZIRCONIUM
+Element.IODINE
+Element.PLATINUM
+Element.GOLD
+Element.MERCURY
+```
+## Methods
+
+:::group{name=getElectronegativity}
+
+Return Type: float?
+
+```zenscript
+// Element.getElectronegativity() as float?
+
+myElement.getElectronegativity();
+```
+
+:::
+
+:::group{name=getGeometry}
+
+Return Type: **invalid**
+
+```zenscript
+Element.getGeometry(connections as int) as invalid
+```
+
+| Parameter | Type |
+|-------------|------|
+| connections | int |
+
+
+:::
+
+:::group{name=getMass}
+
+Return Type: float?
+
+```zenscript
+// Element.getMass() as float?
+
+myElement.getMass();
+```
+
+:::
+
+:::group{name=getMaxValency}
+
+Return Type: double
+
+```zenscript
+// Element.getMaxValency() as double
+
+myElement.getMaxValency();
+```
+
+:::
+
+:::group{name=getNextLowestValency}
+
+Return Type: double
+
+```zenscript
+Element.getNextLowestValency(valency as double) as double
+```
+
+| Parameter | Type |
+|-----------|--------|
+| valency | double |
+
+
+:::
+
+:::group{name=getPartial}
+
+Return Type: **invalid**
+
+```zenscript
+// Element.getPartial() as invalid
+
+myElement.getPartial();
+```
+
+:::
+
+:::group{name=getSymbol}
+
+Return Type: string
+
+```zenscript
+// Element.getSymbol() as string
+
+myElement.getSymbol();
+```
+
+:::
+
+:::group{name=isValidValency}
+
+Return Type: boolean
+
+```zenscript
+Element.isValidValency(valency as double) as boolean
+```
+
+| Parameter | Type |
+|-----------|--------|
+| valency | double |
+
+
+:::
+
+:::group{name=setPartial}
+
+```zenscript
+Element.setPartial(partial as invalid)
+```
+
+| Parameter | Type |
+|-----------|-------------|
+| partial | **invalid** |
+
+
+:::
+
+
diff --git a/docsOut/docs/mods/destroy/Formula.json b/docsOut/docs/mods/destroy/Formula.json
new file mode 100644
index 000000000..d67832ae0
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Formula.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/Formula.md","zenCodeName":"mods.destroy.Formula","searchTerms":["alcohol","addCarbonyl","getCarbocationStability","replace","getBondedAtomsOfElement","addAllHydrogens","joinFormulae","remove","carbonChain","replaceBondTo","addAtom","getAllAtoms","isCyclic","getTotalBonds","toString","atom","addGroup","setStartingAtom","moveTo","deserialize"]}
diff --git a/docsOut/docs/mods/destroy/Formula.md b/docsOut/docs/mods/destroy/Formula.md
new file mode 100644
index 000000000..a5a15dcb5
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Formula.md
@@ -0,0 +1,398 @@
+# Formula
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.Formula;
+```
+
+
+## Implemented Interfaces
+Formula implements the following interfaces. That means all methods defined in these interfaces are also available in Formula
+
+- Cloneable
+
+## Static Methods
+
+:::group{name=alcohol}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+// Formula.alcohol() as Formula
+
+Formula.alcohol();
+```
+
+:::
+
+:::group{name=atom}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.atom(element as Element) as Formula
+```
+
+| Parameter | Type |
+|-----------|----------------------------------|
+| element | [Element](/mods/destroy/Element) |
+
+
+:::
+
+:::group{name=carbonChain}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.carbonChain(length as int) as Formula
+```
+
+| Parameter | Type |
+|-----------|------|
+| length | int |
+
+
+:::
+
+:::group{name=deserialize}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.deserialize(FROWNSstring as string) as Formula
+```
+
+| Parameter | Type |
+|--------------|--------|
+| FROWNSstring | string |
+
+
+:::
+
+## Constructors
+
+
+```zenscript
+new Formula(startingAtom as Atom) as Formula
+```
+| Parameter | Type |
+|--------------|----------------------------|
+| startingAtom | [Atom](/mods/destroy/Atom) |
+
+
+
+## Methods
+
+:::group{name=addAllHydrogens}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+// Formula.addAllHydrogens() as Formula
+
+myFormula.addAllHydrogens();
+```
+
+:::
+
+:::group{name=addAtom}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.addAtom(atom as Atom) as Formula
+```
+
+| Parameter | Type |
+|-----------|----------------------------|
+| atom | [Atom](/mods/destroy/Atom) |
+
+
+:::
+
+:::group{name=addAtom}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.addAtom(element as Element) as Formula
+```
+
+| Parameter | Type |
+|-----------|----------------------------------|
+| element | [Element](/mods/destroy/Element) |
+
+
+:::
+
+:::group{name=addAtom}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.addAtom(atom as Atom, bondType as invalid) as Formula
+```
+
+| Parameter | Type |
+|-----------|----------------------------|
+| atom | [Atom](/mods/destroy/Atom) |
+| bondType | **invalid** |
+
+
+:::
+
+:::group{name=addAtom}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.addAtom(element as Element, bondType as invalid) as Formula
+```
+
+| Parameter | Type |
+|-----------|----------------------------------|
+| element | [Element](/mods/destroy/Element) |
+| bondType | **invalid** |
+
+
+:::
+
+:::group{name=addCarbonyl}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+// Formula.addCarbonyl() as Formula
+
+myFormula.addCarbonyl();
+```
+
+:::
+
+:::group{name=addGroup}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.addGroup(group as Formula) as Formula
+```
+
+| Parameter | Type |
+|-----------|----------------------------------|
+| group | [Formula](/mods/destroy/Formula) |
+
+
+:::
+
+:::group{name=addGroup}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.addGroup(group as Formula, isSideGroup as boolean) as Formula
+```
+
+| Parameter | Type |
+|-------------|----------------------------------|
+| group | [Formula](/mods/destroy/Formula) |
+| isSideGroup | boolean |
+
+
+:::
+
+:::group{name=addGroup}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.addGroup(group as Formula, isSideGroup as boolean, bondType as invalid) as Formula
+```
+
+| Parameter | Type |
+|-------------|----------------------------------|
+| group | [Formula](/mods/destroy/Formula) |
+| isSideGroup | boolean |
+| bondType | **invalid** |
+
+
+:::
+
+:::group{name=getAllAtoms}
+
+Return Type: Set<[Atom](/mods/destroy/Atom)>
+
+```zenscript
+// Formula.getAllAtoms() as Set
+
+myFormula.getAllAtoms();
+```
+
+:::
+
+:::group{name=getBondedAtomsOfElement}
+
+Return Type: stdlib.List<[Atom](/mods/destroy/Atom)>
+
+```zenscript
+Formula.getBondedAtomsOfElement(element as Element) as stdlib.List
+```
+
+| Parameter | Type |
+|-----------|----------------------------------|
+| element | [Element](/mods/destroy/Element) |
+
+
+:::
+
+:::group{name=getCarbocationStability}
+
+Return Type: float
+
+```zenscript
+Formula.getCarbocationStability(carbon as Atom, isCarbanion as boolean) as float
+```
+
+| Parameter | Type |
+|-------------|----------------------------|
+| carbon | [Atom](/mods/destroy/Atom) |
+| isCarbanion | boolean |
+
+
+:::
+
+:::group{name=getTotalBonds}
+
+Return Type: double
+
+```zenscript
+Formula.getTotalBonds(bonds as stdlib.List) as double
+```
+
+| Parameter | Type |
+|-----------|-------------|
+| bonds | stdlib.List |
+
+
+:::
+
+:::group{name=isCyclic}
+
+Return Type: boolean
+
+```zenscript
+// Formula.isCyclic() as boolean
+
+myFormula.isCyclic();
+```
+
+:::
+
+:::group{name=joinFormulae}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.joinFormulae(formula2 as Formula, bondType as invalid) as Formula
+```
+
+| Parameter | Type |
+|-----------|----------------------------------|
+| formula2 | [Formula](/mods/destroy/Formula) |
+| bondType | **invalid** |
+
+
+:::
+
+:::group{name=moveTo}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.moveTo(atom as Atom) as Formula
+```
+
+| Parameter | Type |
+|-----------|----------------------------|
+| atom | [Atom](/mods/destroy/Atom) |
+
+
+:::
+
+:::group{name=remove}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.remove(atom as Atom) as Formula
+```
+
+| Parameter | Type |
+|-----------|----------------------------|
+| atom | [Atom](/mods/destroy/Atom) |
+
+
+:::
+
+:::group{name=replace}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.replace(oldAtom as Atom, newAtom as Atom) as Formula
+```
+
+| Parameter | Type |
+|-----------|----------------------------|
+| oldAtom | [Atom](/mods/destroy/Atom) |
+| newAtom | [Atom](/mods/destroy/Atom) |
+
+
+:::
+
+:::group{name=replaceBondTo}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.replaceBondTo(otherAtom as Atom, bondType as invalid) as Formula
+```
+
+| Parameter | Type |
+|-----------|----------------------------|
+| otherAtom | [Atom](/mods/destroy/Atom) |
+| bondType | **invalid** |
+
+
+:::
+
+:::group{name=setStartingAtom}
+
+Return Type: [Formula](/mods/destroy/Formula)
+
+```zenscript
+Formula.setStartingAtom(atom as Atom) as Formula
+```
+
+| Parameter | Type |
+|-----------|----------------------------|
+| atom | [Atom](/mods/destroy/Atom) |
+
+
+:::
+
+:::group{name=toString}
+
+Return Type: string
+
+```zenscript
+// Formula.toString() as string
+
+myFormula.toString();
+```
+
+:::
+
+
diff --git a/docsOut/docs/mods/destroy/IDestroyRecipeManager.json b/docsOut/docs/mods/destroy/IDestroyRecipeManager.json
new file mode 100644
index 000000000..01f2bd647
--- /dev/null
+++ b/docsOut/docs/mods/destroy/IDestroyRecipeManager.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/IDestroyRecipeManager.md","zenCodeName":"mods.destroy.IDestroyRecipeManager","searchTerms":["getRecipeByName","addJsonRecipe","allRecipes","removeByModid","remove","recipeMap","removeAll","removeByName","getRecipeMap","getAllRecipes","registerRecipe","removeByInput","getRecipesByOutput","removeByRegex"]}
diff --git a/docsOut/docs/mods/destroy/IDestroyRecipeManager.md b/docsOut/docs/mods/destroy/IDestroyRecipeManager.md
new file mode 100644
index 000000000..d47c2dd1b
--- /dev/null
+++ b/docsOut/docs/mods/destroy/IDestroyRecipeManager.md
@@ -0,0 +1,186 @@
+# IDestroyRecipeManager<T : invalid>
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.IDestroyRecipeManager;
+```
+
+
+## Implemented Interfaces
+IDestroyRecipeManager implements the following interfaces. That means all methods defined in these interfaces are also available in IDestroyRecipeManager
+
+- [IRecipeManager](/vanilla/api/recipe/manager/IRecipeManager)<T>
+
+## Methods
+
+:::group{name=addJsonRecipe}
+
+```zenscript
+IDestroyRecipeManager.addJsonRecipe(name as string, mapData as MapData)
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------|
+| name | string |
+| mapData | [MapData](/vanilla/api/data/MapData) |
+
+
+:::
+
+:::group{name=getAllRecipes}
+
+Return Type: stdlib.List<T>
+
+```zenscript
+// IDestroyRecipeManager.getAllRecipes() as stdlib.List
+
+myIDestroyRecipeManager.getAllRecipes();
+```
+
+:::
+
+:::group{name=getRecipeByName}
+
+Return Type: T
+
+```zenscript
+IDestroyRecipeManager.getRecipeByName(name as string) as T
+```
+
+| Parameter | Type |
+|-----------|--------|
+| name | string |
+
+
+:::
+
+:::group{name=getRecipeMap}
+
+Return Type: T[[ResourceLocation](/vanilla/api/resource/ResourceLocation)]
+
+```zenscript
+// IDestroyRecipeManager.getRecipeMap() as T[ResourceLocation]
+
+myIDestroyRecipeManager.getRecipeMap();
+```
+
+:::
+
+:::group{name=getRecipesByOutput}
+
+Return Type: stdlib.List<T>
+
+```zenscript
+IDestroyRecipeManager.getRecipesByOutput(output as IIngredient) as stdlib.List
+```
+
+| Parameter | Type |
+|-----------|----------------------------------------------------|
+| output | [IIngredient](/vanilla/api/ingredient/IIngredient) |
+
+
+:::
+
+:::group{name=registerRecipe}
+
+Registers a recipe using a builder approach.
+
+```zenscript
+IDestroyRecipeManager.registerRecipe(name as string, recipeBuilder as Consumer)
+```
+
+| Parameter | Type | Description |
+|---------------|----------|-------------------------|
+| name | string | The name of the recipe. |
+| recipeBuilder | Consumer | The recipe builder. |
+
+
+:::
+
+:::group{name=remove}
+
+```zenscript
+IDestroyRecipeManager.remove(output as IIngredient)
+```
+
+| Parameter | Type |
+|-----------|----------------------------------------------------|
+| output | [IIngredient](/vanilla/api/ingredient/IIngredient) |
+
+
+:::
+
+:::group{name=removeAll}
+
+```zenscript
+// IDestroyRecipeManager.removeAll()
+
+myIDestroyRecipeManager.removeAll();
+```
+
+:::
+
+:::group{name=removeByInput}
+
+```zenscript
+IDestroyRecipeManager.removeByInput(input as IItemStack)
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------------|
+| input | [IItemStack](/vanilla/api/item/IItemStack) |
+
+
+:::
+
+:::group{name=removeByModid}
+
+```zenscript
+IDestroyRecipeManager.removeByModid(modid as string, exclude as Predicate)
+```
+
+| Parameter | Type | Optional | Default Value |
+|-----------|-------------------------|----------|-----------------------------------|
+| modid | string | false | |
+| exclude | Predicate<string> | true | (name as string) as bool => false |
+
+
+:::
+
+:::group{name=removeByName}
+
+```zenscript
+IDestroyRecipeManager.removeByName(names as string[])
+```
+
+| Parameter | Type |
+|-----------|----------|
+| names | string[] |
+
+
+:::
+
+:::group{name=removeByRegex}
+
+```zenscript
+IDestroyRecipeManager.removeByRegex(regex as string, exclude as Predicate)
+```
+
+| Parameter | Type | Optional | Default Value |
+|-----------|-------------------------|----------|-----------------------------------|
+| regex | string | false | |
+| exclude | Predicate<string> | true | (name as string) as bool => false |
+
+
+:::
+
+
+## Properties
+
+| Name | Type | Has Getter | Has Setter |
+|------------|---------------------------------------------------------------|------------|------------|
+| allRecipes | stdlib.List<T> | true | false |
+| recipeMap | T[[ResourceLocation](/vanilla/api/resource/ResourceLocation)] | true | false |
+
diff --git a/docsOut/docs/mods/destroy/Mixture.json b/docsOut/docs/mods/destroy/Mixture.json
new file mode 100644
index 000000000..d481f9e5c
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Mixture.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/Mixture.md","zenCodeName":"mods.destroy.Mixture","searchTerms":["hasUsableMolecules","heat","getVolumetricHeatCapacity","setTranslationKey","scale","pure","getConcentrationOf","temperature","createMixtureStack","getContents","create","setTemperature","createSaltFluidIngredient","mix","hasUsableMolecule","createIonFluidIngredient","createMoleculeTagFluidIngredient","isEmpty","getColor","isAtEquilibrium","addMolecule","getTotalConcentration","getContentsString","disturbEquilibrium","getContentsTooltip","getTemperature","createMoleculeFluidIngredient"]}
diff --git a/docsOut/docs/mods/destroy/Mixture.md b/docsOut/docs/mods/destroy/Mixture.md
new file mode 100644
index 000000000..fbfefc68a
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Mixture.md
@@ -0,0 +1,425 @@
+# Mixture
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.Mixture;
+```
+
+
+## Static Methods
+
+:::group{name=create}
+
+
+
+Return Type: [Mixture](/mods/destroy/Mixture)
+
+```zenscript
+Mixture.create(data as stdlib.List>, temperature as float, translationKey as string) as Mixture
+```
+
+| Parameter | Type | Description | Optional | Default Value |
+|----------------|------------------------------------------------------------------------------------------------------------------|-------------------------------------|----------|---------------|
+| data | stdlib.List<[Percentaged](/vanilla/api/util/random/Percentaged)<[Molecule](/mods/destroy/Molecule)>> | data of molecules and concentration | false | |
+| temperature | float | In kelvins, 0 for unspecified | true | 0.0 |
+| translationKey | string | | true | |
+
+
+:::
+
+:::group{name=createIonFluidIngredient}
+
+Return Type: [FluidIngredient](/forge/api/fluid/FluidIngredient)
+
+```zenscript
+Mixture.createIonFluidIngredient(concentration as float, amount as int) as FluidIngredient
+```
+
+| Parameter | Type | Optional | Default Value |
+|---------------|-------|----------|---------------|
+| concentration | float | false | |
+| amount | int | true | 1000 |
+
+
+:::
+
+:::group{name=createMixtureStack}
+
+Return Type: [IFluidStack](/vanilla/api/fluid/IFluidStack)
+
+```zenscript
+Mixture.createMixtureStack(mixture as Mixture, amount as int) as IFluidStack
+```
+
+| Parameter | Type | Optional | Default Value |
+|-----------|----------------------------------|----------|---------------|
+| mixture | [Mixture](/mods/destroy/Mixture) | false | |
+| amount | int | true | 1000 |
+
+
+:::
+
+:::group{name=createMoleculeFluidIngredient}
+
+Return Type: [FluidIngredient](/forge/api/fluid/FluidIngredient)
+
+```zenscript
+Mixture.createMoleculeFluidIngredient(moleculeData as Percentaged, amount as int) as FluidIngredient
+```
+
+| Parameter | Type | Optional | Default Value |
+|--------------|-----------------------------------------------------------------------------------------------|----------|---------------|
+| moleculeData | [Percentaged](/vanilla/api/util/random/Percentaged)<[Molecule](/mods/destroy/Molecule)> | false | |
+| amount | int | true | 1000 |
+
+
+:::
+
+:::group{name=createMoleculeFluidIngredient}
+
+Return Type: [FluidIngredient](/forge/api/fluid/FluidIngredient)
+
+```zenscript
+Mixture.createMoleculeFluidIngredient(molecule as Molecule, concentration as float, amount as int) as FluidIngredient
+```
+
+| Parameter | Type | Optional | Default Value |
+|---------------|------------------------------------|----------|---------------|
+| molecule | [Molecule](/mods/destroy/Molecule) | false | |
+| concentration | float | false | |
+| amount | int | true | 1000 |
+
+
+:::
+
+:::group{name=createMoleculeTagFluidIngredient}
+
+Return Type: [FluidIngredient](/forge/api/fluid/FluidIngredient)
+
+```zenscript
+Mixture.createMoleculeTagFluidIngredient(tag as invalid, concentration as float, amount as int) as FluidIngredient
+```
+
+| Parameter | Type | Optional | Default Value |
+|---------------|-------------|----------|---------------|
+| tag | **invalid** | false | |
+| concentration | float | false | |
+| amount | int | true | 1000 |
+
+
+:::
+
+:::group{name=createSaltFluidIngredient}
+
+Return Type: [FluidIngredient](/forge/api/fluid/FluidIngredient)
+
+```zenscript
+Mixture.createSaltFluidIngredient(cation as Molecule, anion as Molecule, concentration as float, amount as int) as FluidIngredient
+```
+
+| Parameter | Type | Optional | Default Value |
+|---------------|------------------------------------|----------|---------------|
+| cation | [Molecule](/mods/destroy/Molecule) | false | |
+| anion | [Molecule](/mods/destroy/Molecule) | false | |
+| concentration | float | false | |
+| amount | int | true | 1000 |
+
+
+:::
+
+:::group{name=mix}
+
+Return Type: [Mixture](/mods/destroy/Mixture)
+
+```zenscript
+Mixture.mix(mixtures as double?[Mixture]) as Mixture
+```
+
+| Parameter | Type |
+|-----------|-------------------------------------------|
+| mixtures | double?[[Mixture](/mods/destroy/Mixture)] |
+
+
+:::
+
+:::group{name=pure}
+
+Return Type: [Mixture](/mods/destroy/Mixture)
+
+```zenscript
+Mixture.pure(molecule as Molecule) as Mixture
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) |
+
+
+:::
+
+## Casters
+
+| Result Type | Is Implicit |
+|-----------------------------------------------|-------------|
+| [IFluidStack](/vanilla/api/fluid/IFluidStack) | true |
+
+## Methods
+
+:::group{name=addMolecule}
+
+Return Type: [Mixture](/mods/destroy/Mixture)
+
+```zenscript
+Mixture.addMolecule(molecule as Molecule, concentration as float) as Mixture
+```
+
+| Parameter | Type |
+|---------------|------------------------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) |
+| concentration | float |
+
+
+:::
+
+:::group{name=disturbEquilibrium}
+
+```zenscript
+// Mixture.disturbEquilibrium()
+
+myMixture.disturbEquilibrium();
+```
+
+:::
+
+:::group{name=getColor}
+
+Return Type: int
+
+```zenscript
+// Mixture.getColor() as int
+
+myMixture.getColor();
+```
+
+:::
+
+:::group{name=getConcentrationOf}
+
+Return Type: float
+
+```zenscript
+Mixture.getConcentrationOf(molecule as Molecule) as float
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) |
+
+
+:::
+
+:::group{name=getContents}
+
+Return Type: stdlib.List<[Molecule](/mods/destroy/Molecule)>
+
+```zenscript
+Mixture.getContents(excludeNovel as boolean) as stdlib.List
+```
+
+| Parameter | Type |
+|--------------|---------|
+| excludeNovel | boolean |
+
+
+:::
+
+:::group{name=getContentsString}
+
+Return Type: string
+
+```zenscript
+// Mixture.getContentsString() as string
+
+myMixture.getContentsString();
+```
+
+:::
+
+:::group{name=getContentsTooltip}
+
+Return Type: stdlib.List<[Component](/vanilla/api/text/Component)>
+
+```zenscript
+Mixture.getContentsTooltip(iupac as boolean, monospace as boolean, useMoles as boolean, amount as int, concentrationFormatter as invalid) as stdlib.List
+```
+
+| Parameter | Type |
+|------------------------|-------------|
+| iupac | boolean |
+| monospace | boolean |
+| useMoles | boolean |
+| amount | int |
+| concentrationFormatter | **invalid** |
+
+
+:::
+
+:::group{name=getTemperature}
+
+Return Type: float
+
+```zenscript
+// Mixture.getTemperature() as float
+
+myMixture.getTemperature();
+```
+
+:::
+
+:::group{name=getTotalConcentration}
+
+Return Type: float
+
+```zenscript
+// Mixture.getTotalConcentration() as float
+
+myMixture.getTotalConcentration();
+```
+
+:::
+
+:::group{name=getVolumetricHeatCapacity}
+
+Return Type: float
+
+```zenscript
+// Mixture.getVolumetricHeatCapacity() as float
+
+myMixture.getVolumetricHeatCapacity();
+```
+
+:::
+
+:::group{name=hasUsableMolecule}
+
+Return Type: boolean
+
+```zenscript
+Mixture.hasUsableMolecule(molecule as Molecule, minConcentration as float, maxConcentration as float, ignore as Predicate) as boolean
+```
+
+| Parameter | Type |
+|------------------|-----------------------------------------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) |
+| minConcentration | float |
+| maxConcentration | float |
+| ignore | Predicate<[Molecule](/mods/destroy/Molecule)> |
+
+
+:::
+
+:::group{name=hasUsableMolecules}
+
+Return Type: boolean
+
+```zenscript
+Mixture.hasUsableMolecules(molecules as Predicate, minConcentration as float, maxConcentration as float, ignore as Predicate) as boolean
+```
+
+| Parameter | Type |
+|------------------|-----------------------------------------------------|
+| molecules | Predicate<[Molecule](/mods/destroy/Molecule)> |
+| minConcentration | float |
+| maxConcentration | float |
+| ignore | Predicate<[Molecule](/mods/destroy/Molecule)> |
+
+
+:::
+
+:::group{name=heat}
+
+```zenscript
+Mixture.heat(energyDensity as float)
+```
+
+| Parameter | Type |
+|---------------|-------|
+| energyDensity | float |
+
+
+:::
+
+:::group{name=isAtEquilibrium}
+
+Return Type: boolean
+
+```zenscript
+// Mixture.isAtEquilibrium() as boolean
+
+myMixture.isAtEquilibrium();
+```
+
+:::
+
+:::group{name=isEmpty}
+
+Return Type: boolean
+
+```zenscript
+// Mixture.isEmpty() as boolean
+
+myMixture.isEmpty();
+```
+
+:::
+
+:::group{name=scale}
+
+```zenscript
+Mixture.scale(volumeIncreaseFactor as float)
+```
+
+| Parameter | Type |
+|----------------------|-------|
+| volumeIncreaseFactor | float |
+
+
+:::
+
+:::group{name=setTemperature}
+
+Return Type: [Mixture](/mods/destroy/Mixture)
+
+```zenscript
+Mixture.setTemperature(temperature as float) as Mixture
+```
+
+| Parameter | Type |
+|-------------|-------|
+| temperature | float |
+
+
+:::
+
+:::group{name=setTranslationKey}
+
+```zenscript
+Mixture.setTranslationKey(translationKey as string)
+```
+
+| Parameter | Type |
+|----------------|--------|
+| translationKey | string |
+
+
+:::
+
+
+## Properties
+
+| Name | Type | Has Getter | Has Setter |
+|-------------|----------------------------------|------------|------------|
+| temperature | [Mixture](/mods/destroy/Mixture) | false | true |
+
diff --git a/docsOut/docs/mods/destroy/Molecule.json b/docsOut/docs/mods/destroy/Molecule.json
new file mode 100644
index 000000000..94b0949dc
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Molecule.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/Molecule.md","zenCodeName":"mods.destroy.Molecule","searchTerms":["getName","getCharge","getStructuralFormula","getReactantReactions","getId","getTags","commandString","getEquivalent","getProductReactions","addReactantReaction","getPureConcentration","isNovel","getCarbocationStability","hasTag","getAtoms","getTranslationKey","getMass","isColorless","getColor","getDipoleMoment","getFROWNSCode","getDensity","addProductReaction","isHypothetical","getLatentHeat","getBoilingPoint","isCyclic","getMolarHeatCapacity"]}
diff --git a/docsOut/docs/mods/destroy/Molecule.md b/docsOut/docs/mods/destroy/Molecule.md
new file mode 100644
index 000000000..01767a593
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Molecule.md
@@ -0,0 +1,373 @@
+# Molecule
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.Molecule;
+```
+
+
+## Casters
+
+| Result Type | Is Implicit |
+|-----------------------------------------------------------------------------------------------|-------------|
+| [Percentaged](/vanilla/api/util/random/Percentaged)<[Molecule](/mods/destroy/Molecule)> | true |
+
+## Methods
+
+:::group{name=addProductReaction}
+
+Mark this Molecule as being a necessary reactant in the given **invalid**.
+ There should never be any need to call this method (it is done automatically when **invalid**#build building a Reaction).
+
+```zenscript
+Molecule.addProductReaction(reaction as Reaction)
+```
+
+| Parameter | Type | Description |
+|-----------|------------------------------------|-------------|
+| reaction | [Reaction](/mods/destroy/Reaction) | Reaction |
+
+
+:::
+
+:::group{name=addReactantReaction}
+
+```zenscript
+Molecule.addReactantReaction(reaction as Reaction)
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| reaction | [Reaction](/mods/destroy/Reaction) |
+
+
+:::
+
+:::group{name=getAtoms}
+
+Return Type: Set<[Atom](/mods/destroy/Atom)>
+
+```zenscript
+// Molecule.getAtoms() as Set
+
+myMolecule.getAtoms();
+```
+
+:::
+
+:::group{name=getBoilingPoint}
+
+Return Type: float
+
+```zenscript
+// Molecule.getBoilingPoint() as float
+
+myMolecule.getBoilingPoint();
+```
+
+:::
+
+:::group{name=getCarbocationStability}
+
+Return Type: float
+
+```zenscript
+Molecule.getCarbocationStability(carbon as Atom, isCarbanion as boolean) as float
+```
+
+| Parameter | Type |
+|-------------|----------------------------|
+| carbon | [Atom](/mods/destroy/Atom) |
+| isCarbanion | boolean |
+
+
+:::
+
+:::group{name=getCharge}
+
+Return Type: int
+
+```zenscript
+// Molecule.getCharge() as int
+
+myMolecule.getCharge();
+```
+
+:::
+
+:::group{name=getColor}
+
+Return Type: int
+
+```zenscript
+// Molecule.getColor() as int
+
+myMolecule.getColor();
+```
+
+:::
+
+:::group{name=getDensity}
+
+Return Type: float
+
+```zenscript
+// Molecule.getDensity() as float
+
+myMolecule.getDensity();
+```
+
+:::
+
+:::group{name=getDipoleMoment}
+
+Return Type: float
+
+```zenscript
+// Molecule.getDipoleMoment() as float
+
+myMolecule.getDipoleMoment();
+```
+
+:::
+
+:::group{name=getEquivalent}
+
+Return Type: [Molecule](/mods/destroy/Molecule)
+
+```zenscript
+// Molecule.getEquivalent() as Molecule
+
+myMolecule.getEquivalent();
+```
+
+:::
+
+:::group{name=getFROWNSCode}
+
+Return Type: string
+
+```zenscript
+// Molecule.getFROWNSCode() as string
+
+myMolecule.getFROWNSCode();
+```
+
+:::
+
+:::group{name=getId}
+
+Return Type: string
+
+```zenscript
+// Molecule.getId() as string
+
+myMolecule.getId();
+```
+
+:::
+
+:::group{name=getLatentHeat}
+
+Return Type: float
+
+```zenscript
+// Molecule.getLatentHeat() as float
+
+myMolecule.getLatentHeat();
+```
+
+:::
+
+:::group{name=getMass}
+
+Return Type: float
+
+```zenscript
+// Molecule.getMass() as float
+
+myMolecule.getMass();
+```
+
+:::
+
+:::group{name=getMolarHeatCapacity}
+
+Return Type: float
+
+```zenscript
+// Molecule.getMolarHeatCapacity() as float
+
+myMolecule.getMolarHeatCapacity();
+```
+
+:::
+
+:::group{name=getName}
+
+Return Type: [Component](/vanilla/api/text/Component)
+
+```zenscript
+Molecule.getName(iupac as boolean) as Component
+```
+
+| Parameter | Type |
+|-----------|---------|
+| iupac | boolean |
+
+
+:::
+
+:::group{name=getProductReactions}
+
+Get the list of **invalid** by which this Molecule is made.
+
+Returns: List of Reactions ordered by declaration
+Return Type: stdlib.List<[Reaction](/mods/destroy/Reaction)>
+
+```zenscript
+// Molecule.getProductReactions() as stdlib.List
+
+myMolecule.getProductReactions();
+```
+
+:::
+
+:::group{name=getPureConcentration}
+
+Return Type: float
+
+```zenscript
+// Molecule.getPureConcentration() as float
+
+myMolecule.getPureConcentration();
+```
+
+:::
+
+:::group{name=getReactantReactions}
+
+Get the list of **invalid** of which this Molecule is a necessary Reactant.
+
+Returns: List of Reactions ordered by declaration
+Return Type: stdlib.List<[Reaction](/mods/destroy/Reaction)>
+
+```zenscript
+// Molecule.getReactantReactions() as stdlib.List
+
+myMolecule.getReactantReactions();
+```
+
+:::
+
+:::group{name=getStructuralFormula}
+
+Return Type: string
+
+```zenscript
+// Molecule.getStructuralFormula() as string
+
+myMolecule.getStructuralFormula();
+```
+
+:::
+
+:::group{name=getTags}
+
+Return Type: Set
+
+```zenscript
+// Molecule.getTags() as Set
+
+myMolecule.getTags();
+```
+
+:::
+
+:::group{name=getTranslationKey}
+
+Return Type: string
+
+```zenscript
+Molecule.getTranslationKey(iupac as boolean) as string
+```
+
+| Parameter | Type |
+|-----------|---------|
+| iupac | boolean |
+
+
+:::
+
+:::group{name=hasTag}
+
+Return Type: boolean
+
+```zenscript
+Molecule.hasTag(tag as invalid) as boolean
+```
+
+| Parameter | Type |
+|-----------|-------------|
+| tag | **invalid** |
+
+
+:::
+
+:::group{name=isColorless}
+
+Return Type: boolean
+
+```zenscript
+// Molecule.isColorless() as boolean
+
+myMolecule.isColorless();
+```
+
+:::
+
+:::group{name=isCyclic}
+
+Return Type: boolean
+
+```zenscript
+// Molecule.isCyclic() as boolean
+
+myMolecule.isCyclic();
+```
+
+:::
+
+:::group{name=isHypothetical}
+
+Return Type: boolean
+
+```zenscript
+// Molecule.isHypothetical() as boolean
+
+myMolecule.isHypothetical();
+```
+
+:::
+
+:::group{name=isNovel}
+
+Return Type: boolean
+
+```zenscript
+// Molecule.isNovel() as boolean
+
+myMolecule.isNovel();
+```
+
+:::
+
+
+## Properties
+
+| Name | Type | Has Getter | Has Setter |
+|---------------|--------|------------|------------|
+| commandString | string | true | false |
+
diff --git a/docsOut/docs/mods/destroy/MoleculeBuilder.json b/docsOut/docs/mods/destroy/MoleculeBuilder.json
new file mode 100644
index 000000000..fbeb5d04e
--- /dev/null
+++ b/docsOut/docs/mods/destroy/MoleculeBuilder.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/MoleculeBuilder.md","zenCodeName":"mods.destroy.MoleculeBuilder","searchTerms":["charge","color","density","translationKey","specificHeatCapacity","boilingPointInKelvins","boilingPoint","structure","build","dipoleMoment","hypothetical","formula","molarHeatCapacity","id","tag","latentHeat"]}
diff --git a/docsOut/docs/mods/destroy/MoleculeBuilder.md b/docsOut/docs/mods/destroy/MoleculeBuilder.md
new file mode 100644
index 000000000..bf1cc926a
--- /dev/null
+++ b/docsOut/docs/mods/destroy/MoleculeBuilder.md
@@ -0,0 +1,247 @@
+# MoleculeBuilder
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.MoleculeBuilder;
+```
+
+
+## Methods
+
+:::group{name=boilingPoint}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.boilingPoint(boilingPoint as float) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|--------------|-------|
+| boilingPoint | float |
+
+
+:::
+
+:::group{name=boilingPointInKelvins}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.boilingPointInKelvins(boilingPoint as float) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|--------------|-------|
+| boilingPoint | float |
+
+
+:::
+
+:::group{name=build}
+
+Return Type: [Molecule](/mods/destroy/Molecule)
+
+```zenscript
+// MoleculeBuilder.build() as Molecule
+
+myMoleculeBuilder.build();
+```
+
+:::
+
+:::group{name=charge}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.charge(charge as int) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|-----------|------|
+| charge | int |
+
+
+:::
+
+:::group{name=color}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.color(color as int) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|-----------|------|
+| color | int |
+
+
+:::
+
+:::group{name=density}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.density(density as float) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|-----------|-------|
+| density | float |
+
+
+:::
+
+:::group{name=dipoleMoment}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.dipoleMoment(dipoleMoment as int) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|--------------|------|
+| dipoleMoment | int |
+
+
+:::
+
+:::group{name=formula}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.formula(formula as string) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|-----------|--------|
+| formula | string |
+
+
+:::
+
+:::group{name=hypothetical}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+// MoleculeBuilder.hypothetical() as MoleculeBuilder
+
+myMoleculeBuilder.hypothetical();
+```
+
+:::
+
+:::group{name=id}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.id(id as string) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|-----------|--------|
+| id | string |
+
+
+:::
+
+:::group{name=latentHeat}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.latentHeat(latentHeat as float) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|------------|-------|
+| latentHeat | float |
+
+
+:::
+
+:::group{name=molarHeatCapacity}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.molarHeatCapacity(molarHeatCapacity as float) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|-------------------|-------|
+| molarHeatCapacity | float |
+
+
+:::
+
+:::group{name=specificHeatCapacity}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.specificHeatCapacity(specificHeatCapacity as float) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|----------------------|-------|
+| specificHeatCapacity | float |
+
+
+:::
+
+:::group{name=structure}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.structure(formula as Formula) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|-----------|----------------------------------|
+| formula | [Formula](/mods/destroy/Formula) |
+
+
+:::
+
+:::group{name=tag}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.tag(tags as invalid[]) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|-----------|---------------|
+| tags | **invalid**[] |
+
+
+:::
+
+:::group{name=translationKey}
+
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+MoleculeBuilder.translationKey(translationKey as string) as MoleculeBuilder
+```
+
+| Parameter | Type |
+|----------------|--------|
+| translationKey | string |
+
+
+:::
+
+
diff --git a/docsOut/docs/mods/destroy/Molecules.json b/docsOut/docs/mods/destroy/Molecules.json
new file mode 100644
index 000000000..1b912ed3a
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Molecules.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/Molecules.md","zenCodeName":"mods.destroy.Molecules","searchTerms":["getElement","create","removeMolecule","getMoleculeTag","getMolecule","getMoleculeById"]}
diff --git a/docsOut/docs/mods/destroy/Molecules.md b/docsOut/docs/mods/destroy/Molecules.md
new file mode 100644
index 000000000..d2ead1b9d
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Molecules.md
@@ -0,0 +1,125 @@
+# Molecules
+
+Use Molecules to manage molecules of the mod. Use bracket handler to query
+ a molecule. Example:
+
+ When creating a molecule, you may want to specify how does the molecule impact the environment, use
+ bracket handler. Example: (specifies that the molecule is toxic)
+
+ Use [Molecules](/mods/destroy/Molecules)#create(String) to create a **invalid**
+ if you want to create your own molecule
+
+ Use [Molecules](/mods/destroy/Molecules)#removeMolecule(com.petrolpark.destroy.compat.crafttweaker.natives.CTMolecule) to remove a molecule (all reactions involving this molecule will also be removed)
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.Molecules;
+```
+
+
+## Static Methods
+
+:::group{name=create}
+
+Creates a molecule builder. Call .build() to build the molecule
+
+Returns: The **invalid**
+Return Type: [MoleculeBuilder](/mods/destroy/MoleculeBuilder)
+
+```zenscript
+// Molecules.create(id as string) as MoleculeBuilder
+
+Molecules.create("tellurium_copper");
+```
+
+| Parameter | Type | Description |
+|-----------|--------|------------------------|
+| id | string | ID of the new molecule |
+
+
+:::
+
+:::group{name=getElement}
+
+Return Type: [Element](/mods/destroy/Element)
+
+```zenscript
+Molecules.getElement(tokens as string) as Element
+```
+
+| Parameter | Type |
+|-----------|--------|
+| tokens | string |
+
+
+:::
+
+:::group{name=getMolecule}
+
+Return Type: [Molecule](/mods/destroy/Molecule)
+
+```zenscript
+Molecules.getMolecule(tokens as string) as Molecule
+```
+
+| Parameter | Type |
+|-----------|--------|
+| tokens | string |
+
+
+:::
+
+:::group{name=getMoleculeById}
+
+Gets a molecule by full ID
+
+Returns: A **invalid** which was found by ID or null if molecule doesn't exist
+Return Type: [Molecule](/mods/destroy/Molecule)
+
+```zenscript
+// Molecules.getMoleculeById(moleculeId as string) as Molecule
+
+Molecules.getMoleculeById("destroy:water");
+```
+
+| Parameter | Type | Description |
+|------------|--------|-----------------------|
+| moleculeId | string | Molecule ID to search |
+
+
+:::
+
+:::group{name=getMoleculeTag}
+
+Return Type: **invalid**
+
+```zenscript
+Molecules.getMoleculeTag(tokens as string) as invalid
+```
+
+| Parameter | Type |
+|-----------|--------|
+| tokens | string |
+
+
+:::
+
+:::group{name=removeMolecule}
+
+Removes this molecule from registry. This makes all reactions involving this molecule to unregister as well.
+
+```zenscript
+// Molecules.removeMolecule(molecule as Molecule)
+
+Molecules.removeMolecule();
+```
+
+| Parameter | Type | Description |
+|-----------|------------------------------------|-----------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) | **invalid** to remove |
+
+
+:::
+
diff --git a/docsOut/docs/mods/destroy/Reaction.json b/docsOut/docs/mods/destroy/Reaction.json
new file mode 100644
index 000000000..3cf636bd1
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Reaction.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/Reaction.md","zenCodeName":"mods.destroy.Reaction","searchTerms":["consumesItem","containsReactant","getReactantMolarRatio","getRateConstant","getEnthalpyChange","getPreexponentialFactor","getProductMolarRatio","getFullID","hasResult","getID","getOrders","getReactants","includeInJei","getActivationEnergy","getResult","getReverseReactionForDisplay","getItemReactants","getMolesPerItem","needsUV","containsProduct","getProducts","displayAsReversible"]}
diff --git a/docsOut/docs/mods/destroy/Reaction.md b/docsOut/docs/mods/destroy/Reaction.md
new file mode 100644
index 000000000..3061a16ca
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Reaction.md
@@ -0,0 +1,336 @@
+# Reaction
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.Reaction;
+```
+
+
+## Methods
+
+:::group{name=consumesItem}
+
+Whether this Reaction needs any Item Stack as a **invalid**. Even if this is
+ `true`, the Reaction may still have **invalid**#isCatalyst Item Stack catalysts.
+
+Return Type: boolean
+
+```zenscript
+// Reaction.consumesItem() as boolean
+
+myReaction.consumesItem();
+```
+
+:::
+
+:::group{name=containsProduct}
+
+Whether this Molecule is created in this Reaction.
+
+Return Type: boolean
+
+```zenscript
+Reaction.containsProduct(molecule as Molecule) as boolean
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) |
+
+
+:::
+
+:::group{name=containsReactant}
+
+Whether this Molecule gets consumed in this Reaction (does not include catalysts).
+
+Return Type: boolean
+
+```zenscript
+Reaction.containsReactant(molecule as Molecule) as boolean
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) |
+
+
+:::
+
+:::group{name=displayAsReversible}
+
+Whether this Reaction should be displayed in JEI with an equilibrium arrow rather than a normal one.
+
+Return Type: boolean
+
+```zenscript
+// Reaction.displayAsReversible() as boolean
+
+myReaction.displayAsReversible();
+```
+
+:::
+
+:::group{name=getActivationEnergy}
+
+Return Type: float
+
+```zenscript
+// Reaction.getActivationEnergy() as float
+
+myReaction.getActivationEnergy();
+```
+
+:::
+
+:::group{name=getEnthalpyChange}
+
+Return Type: float
+
+```zenscript
+// Reaction.getEnthalpyChange() as float
+
+myReaction.getEnthalpyChange();
+```
+
+:::
+
+:::group{name=getFullID}
+
+Get the fully unique ID for this Reaction, in the format `:
+ `, for example `destroy:chloroform_fluorination`.
+
+Return Type: string
+
+```zenscript
+// Reaction.getFullID() as string
+
+myReaction.getFullID();
+```
+
+:::
+
+:::group{name=getID}
+
+Return Type: string
+
+```zenscript
+// Reaction.getID() as string
+
+myReaction.getID();
+```
+
+:::
+
+:::group{name=getItemReactants}
+
+Get the **invalid** for this Reaction.
+
+Return Type: stdlib.List
+
+```zenscript
+// Reaction.getItemReactants() as stdlib.List
+
+myReaction.getItemReactants();
+```
+
+:::
+
+:::group{name=getMolesPerItem}
+
+Return Type: float
+
+```zenscript
+// Reaction.getMolesPerItem() as float
+
+myReaction.getMolesPerItem();
+```
+
+:::
+
+:::group{name=getOrders}
+
+Get every **invalid** reactant and catalyst in this Reaction, mapped to their
+ orders in the rate equation.
+
+Return Type: int?[[Molecule](/mods/destroy/Molecule)]
+
+```zenscript
+// Reaction.getOrders() as int?[Molecule]
+
+myReaction.getOrders();
+```
+
+:::
+
+:::group{name=getPreexponentialFactor}
+
+Return Type: float
+
+```zenscript
+// Reaction.getPreexponentialFactor() as float
+
+myReaction.getPreexponentialFactor();
+```
+
+:::
+
+:::group{name=getProductMolarRatio}
+
+Get the stoichometric ratio of this **invalid** in this Reaction.
+
+Returns: `0` if this Molecule is not a product
+Return Type: int
+
+```zenscript
+Reaction.getProductMolarRatio(product as Molecule) as int
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| product | [Molecule](/mods/destroy/Molecule) |
+
+
+:::
+
+:::group{name=getProducts}
+
+All Molecules which are created in this Reaction (but not their molar ratios).
+
+Return Type: Set<[Molecule](/mods/destroy/Molecule)>
+
+```zenscript
+// Reaction.getProducts() as Set
+
+myReaction.getProducts();
+```
+
+:::
+
+:::group{name=getRateConstant}
+
+The rate constant of this Reaction at the given temperature.
+
+Return Type: float
+
+```zenscript
+Reaction.getRateConstant(temperature as float) as float
+```
+
+| Parameter | Type | Description |
+|-------------|-------|---------------|
+| temperature | float | (in kelvins). |
+
+
+:::
+
+:::group{name=getReactantMolarRatio}
+
+Get the stoichometric ratio of this **invalid** or catalyst in this Reaction.
+
+Returns: `0` if this Molecule is not a reactant
+Return Type: int
+
+```zenscript
+Reaction.getReactantMolarRatio(reactant as Molecule) as int
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| reactant | [Molecule](/mods/destroy/Molecule) |
+
+
+:::
+
+:::group{name=getReactants}
+
+All Molecules which are consumed in this Reaction (but not their molar ratios).
+
+Return Type: Set<[Molecule](/mods/destroy/Molecule)>
+
+```zenscript
+// Reaction.getReactants() as Set
+
+myReaction.getReactants();
+```
+
+:::
+
+:::group{name=getResult}
+
+The **invalid** of this Reaction, which occurs once a set
+ number of moles of Reaction have occured.
+
+Returns: `null` if this Reaction has no result.
+Return Type: **invalid**
+
+```zenscript
+// Reaction.getResult() as invalid
+
+myReaction.getResult();
+```
+
+:::
+
+:::group{name=getReverseReactionForDisplay}
+
+If this is the 'forward' half of a reversible Reaction, this contains the reverse Reaction. This is so JEI
+ knows the products of the forward Reaction are the reactants of the reverse, and vice versa. If this is not
+ part of a reversible Reaction, this is empty. This is just for display; if a Reaction has a reverse and is needed
+ for logic (e.g. Reacting in a Mixture) it should not be accessed in this way.
+
+Return Type: **invalid**
+
+```zenscript
+// Reaction.getReverseReactionForDisplay() as invalid
+
+myReaction.getReverseReactionForDisplay();
+```
+
+:::
+
+:::group{name=hasResult}
+
+Whether this Reaction has a **invalid**.
+
+Return Type: boolean
+
+```zenscript
+// Reaction.hasResult() as boolean
+
+myReaction.hasResult();
+```
+
+:::
+
+:::group{name=includeInJei}
+
+Whether this Reaction should be displayed in the list of Reactions in JEI.
+
+Return Type: boolean
+
+```zenscript
+// Reaction.includeInJei() as boolean
+
+myReaction.includeInJei();
+```
+
+:::
+
+:::group{name=needsUV}
+
+Whether this Reaction needs UV light to proceed.
+
+Return Type: boolean
+
+```zenscript
+// Reaction.needsUV() as boolean
+
+myReaction.needsUV();
+```
+
+:::
+
+
diff --git a/docsOut/docs/mods/destroy/ReactionBuilder.json b/docsOut/docs/mods/destroy/ReactionBuilder.json
new file mode 100644
index 000000000..d894f7c72
--- /dev/null
+++ b/docsOut/docs/mods/destroy/ReactionBuilder.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/ReactionBuilder.md","zenCodeName":"mods.destroy.ReactionBuilder","searchTerms":["addCatalyst","reverseReaction","addSimpleItemCatalyst","addItemReactant","preexponentialFactor","addProduct","acid","addReactant","setOrder","requireUV","activationEnergy","addSimpleItemReactant","build","dontIncludeInJei","enthalpyChange","id","withResult","displayAsReversible"]}
diff --git a/docsOut/docs/mods/destroy/ReactionBuilder.md b/docsOut/docs/mods/destroy/ReactionBuilder.md
new file mode 100644
index 000000000..c06db6e79
--- /dev/null
+++ b/docsOut/docs/mods/destroy/ReactionBuilder.md
@@ -0,0 +1,328 @@
+# ReactionBuilder
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.ReactionBuilder;
+```
+
+
+## Methods
+
+:::group{name=acid}
+
+Return Type: [Reaction](/mods/destroy/Reaction)
+
+```zenscript
+ReactionBuilder.acid(acid as Molecule, conjugateBase as Molecule, pKa as float) as Reaction
+```
+
+| Parameter | Type |
+|---------------|------------------------------------|
+| acid | [Molecule](/mods/destroy/Molecule) |
+| conjugateBase | [Molecule](/mods/destroy/Molecule) |
+| pKa | float |
+
+
+:::
+
+:::group{name=activationEnergy}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.activationEnergy(activationEnergy as float) as ReactionBuilder
+```
+
+| Parameter | Type |
+|------------------|-------|
+| activationEnergy | float |
+
+
+:::
+
+:::group{name=addCatalyst}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.addCatalyst(molecule as Molecule, order as int) as ReactionBuilder
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) |
+| order | int |
+
+
+:::
+
+:::group{name=addItemReactant}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.addItemReactant(itemReactant as invalid, moles as float) as ReactionBuilder
+```
+
+| Parameter | Type |
+|--------------|-------------|
+| itemReactant | **invalid** |
+| moles | float |
+
+
+:::
+
+:::group{name=addProduct}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.addProduct(molecule as Molecule) as ReactionBuilder
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) |
+
+
+:::
+
+:::group{name=addProduct}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.addProduct(molecule as Molecule, ratio as int) as ReactionBuilder
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) |
+| ratio | int |
+
+
+:::
+
+:::group{name=addReactant}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.addReactant(molecule as Molecule) as ReactionBuilder
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) |
+
+
+:::
+
+:::group{name=addReactant}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.addReactant(molecule as Molecule, ratio as int) as ReactionBuilder
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) |
+| ratio | int |
+
+
+:::
+
+:::group{name=addReactant}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.addReactant(molecule as Molecule, ratio as int, order as int) as ReactionBuilder
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) |
+| ratio | int |
+| order | int |
+
+
+:::
+
+:::group{name=addSimpleItemCatalyst}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.addSimpleItemCatalyst(item as Supplier, moles as float) as ReactionBuilder
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------------------------------------|
+| item | Supplier<[ItemDefinition](/vanilla/api/item/ItemDefinition)> |
+| moles | float |
+
+
+:::
+
+:::group{name=addSimpleItemReactant}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.addSimpleItemReactant(item as Supplier, moles as float) as ReactionBuilder
+```
+
+| Parameter | Type |
+|-----------|--------------------------------------------------------------------|
+| item | Supplier<[ItemDefinition](/vanilla/api/item/ItemDefinition)> |
+| moles | float |
+
+
+:::
+
+:::group{name=build}
+
+Return Type: [Reaction](/mods/destroy/Reaction)
+
+```zenscript
+// ReactionBuilder.build() as Reaction
+
+myReactionBuilder.build();
+```
+
+:::
+
+:::group{name=displayAsReversible}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+// ReactionBuilder.displayAsReversible() as ReactionBuilder
+
+myReactionBuilder.displayAsReversible();
+```
+
+:::
+
+:::group{name=dontIncludeInJei}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+// ReactionBuilder.dontIncludeInJei() as ReactionBuilder
+
+myReactionBuilder.dontIncludeInJei();
+```
+
+:::
+
+:::group{name=enthalpyChange}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.enthalpyChange(enthalpyChange as float) as ReactionBuilder
+```
+
+| Parameter | Type |
+|----------------|-------|
+| enthalpyChange | float |
+
+
+:::
+
+:::group{name=id}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.id(id as string) as ReactionBuilder
+```
+
+| Parameter | Type |
+|-----------|--------|
+| id | string |
+
+
+:::
+
+:::group{name=preexponentialFactor}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.preexponentialFactor(preexponentialFactor as float) as ReactionBuilder
+```
+
+| Parameter | Type |
+|----------------------|-------|
+| preexponentialFactor | float |
+
+
+:::
+
+:::group{name=requireUV}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+// ReactionBuilder.requireUV() as ReactionBuilder
+
+myReactionBuilder.requireUV();
+```
+
+:::
+
+:::group{name=reverseReaction}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.reverseReaction(reverseReactionModifier as Consumer) as ReactionBuilder
+```
+
+| Parameter | Type |
+|-------------------------|------------------------------------------------------------------|
+| reverseReactionModifier | Consumer<[ReactionBuilder](/mods/destroy/ReactionBuilder)> |
+
+
+:::
+
+:::group{name=setOrder}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.setOrder(molecule as Molecule, order as int) as ReactionBuilder
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| molecule | [Molecule](/mods/destroy/Molecule) |
+| order | int |
+
+
+:::
+
+:::group{name=withResult}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+ReactionBuilder.withResult(moles as float, reactionresultFactory as BiFunction) as ReactionBuilder
+```
+
+| Parameter | Type |
+|-----------------------|------------|
+| moles | float |
+| reactionresultFactory | BiFunction |
+
+
+:::
+
+
diff --git a/docsOut/docs/mods/destroy/Reactions.json b/docsOut/docs/mods/destroy/Reactions.json
new file mode 100644
index 000000000..2595557b5
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Reactions.json
@@ -0,0 +1 @@
+{"path":"mods/destroy/Reactions.md","zenCodeName":"mods.destroy.Reactions","searchTerms":["getReactionById","create","getReaction","removeReaction"]}
diff --git a/docsOut/docs/mods/destroy/Reactions.md b/docsOut/docs/mods/destroy/Reactions.md
new file mode 100644
index 000000000..4676889f7
--- /dev/null
+++ b/docsOut/docs/mods/destroy/Reactions.md
@@ -0,0 +1,70 @@
+# Reactions
+
+## Importing the class
+
+It might be required for you to import the package if you encounter any issues (like casting an Array), so better be safe than sorry and add the import at the very top of the file.
+```zenscript
+import mods.destroy.Reactions;
+```
+
+
+## Static Methods
+
+:::group{name=create}
+
+Return Type: [ReactionBuilder](/mods/destroy/ReactionBuilder)
+
+```zenscript
+Reactions.create(id as string) as ReactionBuilder
+```
+
+| Parameter | Type |
+|-----------|--------|
+| id | string |
+
+
+:::
+
+:::group{name=getReaction}
+
+Return Type: [Reaction](/mods/destroy/Reaction)
+
+```zenscript
+Reactions.getReaction(tokens as string) as Reaction
+```
+
+| Parameter | Type |
+|-----------|--------|
+| tokens | string |
+
+
+:::
+
+:::group{name=getReactionById}
+
+Return Type: [Reaction](/mods/destroy/Reaction)
+
+```zenscript
+Reactions.getReactionById(reactionId as string) as Reaction
+```
+
+| Parameter | Type |
+|------------|--------|
+| reactionId | string |
+
+
+:::
+
+:::group{name=removeReaction}
+
+```zenscript
+Reactions.removeReaction(reaction as Reaction)
+```
+
+| Parameter | Type |
+|-----------|------------------------------------|
+| reaction | [Reaction](/mods/destroy/Reaction) |
+
+
+:::
+
diff --git a/gradle.properties b/gradle.properties
index 48a71a10b..f4bed9f63 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -21,4 +21,5 @@ flywheel_version = 0.6.10-7
registrate_version = MC1.20-1.3.3
jei_version = 15.2.0.22
farmersdelight_version = 4638874
-blueprint_version = 7.0.0
\ No newline at end of file
+blueprint_version = 7.0.0
+crafttweaker_version = 14.0.16
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 2d16ed5c3..3ad1106ad 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -2,6 +2,7 @@ pluginManagement {
repositories {
gradlePluginPortal()
maven { url = 'https://maven.minecraftforge.net/' }
+ maven { url = "https://maven.blamejared.com"}
}
}
\ No newline at end of file
diff --git a/src/main/java/com/petrolpark/destroy/block/entity/PollutometerBlockEntity.java b/src/main/java/com/petrolpark/destroy/block/entity/PollutometerBlockEntity.java
index 3d9fd58f6..2c01298e3 100644
--- a/src/main/java/com/petrolpark/destroy/block/entity/PollutometerBlockEntity.java
+++ b/src/main/java/com/petrolpark/destroy/block/entity/PollutometerBlockEntity.java
@@ -56,7 +56,7 @@ public PollutionType getPollutionType() {
return pollutionType;
};
- private void setPollutionType(Integer pollutionTypeIndex) {
+ private void setPollutionType(int pollutionTypeIndex) {
this.pollutionType = PollutionType.values()[pollutionTypeIndex];
};
diff --git a/src/main/java/com/petrolpark/destroy/block/entity/behaviour/ExtendedBasinBehaviour.java b/src/main/java/com/petrolpark/destroy/block/entity/behaviour/ExtendedBasinBehaviour.java
index 6e7f9ccfc..9c9db76d4 100644
--- a/src/main/java/com/petrolpark/destroy/block/entity/behaviour/ExtendedBasinBehaviour.java
+++ b/src/main/java/com/petrolpark/destroy/block/entity/behaviour/ExtendedBasinBehaviour.java
@@ -53,10 +53,12 @@ public void tick() {
};
public void enactReactionResults(BasinBlockEntity basin) {
-
- for (ReactionResult result : reactionResults.keySet()) {
- for (int i = 0; i < reactionResults.get(result); i++) result.onBasinReaction(basin.getLevel(), basin);
- };
+ for(Map.Entry reactionEntry : reactionResults.entrySet()) {
+ ReactionResult result = reactionEntry.getKey();
+ for(int i = 0; i < reactionEntry.getValue(); i++) {
+ result.onBasinReaction(basin.getLevel(), basin);
+ }
+ }
reactionResults.clear();
if (!evaporatedFluid.isEmpty()) {
@@ -103,7 +105,7 @@ public void write(CompoundTag nbt, boolean clientPacket) {
nbt.put("Results", NBTHelper.writeCompoundList(reactionResults.entrySet(), entry -> {
CompoundTag resultTag = new CompoundTag();
- resultTag.putString("Result", entry.getKey().getReaction().getFullId());
+ resultTag.putString("Result", entry.getKey().getReaction().getFullID());
resultTag.putInt("Count", entry.getValue());
return resultTag;
}));
diff --git a/src/main/java/com/petrolpark/destroy/capability/level/pollution/LevelPollution.java b/src/main/java/com/petrolpark/destroy/capability/level/pollution/LevelPollution.java
index 48bc64edf..8ca00b7a2 100644
--- a/src/main/java/com/petrolpark/destroy/capability/level/pollution/LevelPollution.java
+++ b/src/main/java/com/petrolpark/destroy/capability/level/pollution/LevelPollution.java
@@ -123,9 +123,7 @@ public void saveNBTData(CompoundTag tag) {
};
public void loadNBTData(CompoundTag tag) {
- levels.keySet().forEach((pollutionType) -> {
- levels.replace(pollutionType, tag.getInt(pollutionType.name()));
- });
+ levels.replaceAll((pollutionType, level) -> tag.getInt(pollutionType.name()));
hasPollutionEverBeenMaxed = tag.getBoolean("EverMaxed");
hasPollutionEverBeenFullyReduced = tag.getBoolean("EverReduced");
updateTemperature();
@@ -135,8 +133,8 @@ public void updateTemperature() {
outdoorTemperature = 289f;
if (!PollutionHelper.pollutionEnabled() || !DestroyAllConfigs.SERVER.pollution.temperatureAffected.get()) return;
outdoorTemperature +=
- (levels.get(PollutionType.GREENHOUSE) / PollutionType.GREENHOUSE.max) * 20f
- + (levels.get(PollutionType.OZONE_DEPLETION) / PollutionType.OZONE_DEPLETION.max) * 4f;
+ ((float) levels.get(PollutionType.GREENHOUSE) / PollutionType.GREENHOUSE.max) * 20f
+ + ((float) levels.get(PollutionType.OZONE_DEPLETION) / PollutionType.OZONE_DEPLETION.max) * 4f;
};
/**
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/Atom.java b/src/main/java/com/petrolpark/destroy/chemistry/Atom.java
index 337d73835..076bfc5ad 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/Atom.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/Atom.java
@@ -46,7 +46,7 @@ public PartialModel getPartial() {
/**
* Whether this is Atom is a {@link Element#HYDROGEN Hydrogen}.
*/
- public Boolean isHydrogen() {
+ public boolean isHydrogen() {
return element == Element.HYDROGEN;
};
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/Element.java b/src/main/java/com/petrolpark/destroy/chemistry/Element.java
index 9a7e13e01..6f3b3c347 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/Element.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/Element.java
@@ -37,20 +37,20 @@ public enum Element {
//LEAD("Pb", 207.20f, 1.8f)
;
- private String symbol;
- private Float mass;
- private Float electronegativity;
- private double[] valencies;
+ private final String symbol;
+ private final float mass;
+ private final float electronegativity;
+ private final double[] valencies;
- private Function geometryOverride;
+ private final Function geometryOverride;
private PartialModel partial;
- private Element(String symbol, Float mass, Float electronegativity, double[] valencies) {
+ Element(String symbol, Float mass, Float electronegativity, double[] valencies) {
this(symbol, mass, electronegativity, valencies, null);
};
- private Element(String symbol, Float mass, Float electronegativity, double[] valencies, Function geometryOverride) {
+ Element(String symbol, Float mass, Float electronegativity, double[] valencies, Function geometryOverride) {
this.symbol = symbol;
this.mass = mass;
this.electronegativity = electronegativity;
@@ -70,7 +70,7 @@ public Float getElectronegativity() {
return this.electronegativity;
};
;
- public Boolean isValidValency(double valency) {
+ public boolean isValidValency(double valency) {
for (double possibleValency : valencies) {
if (Math.abs(possibleValency - valency) < 0.000001d) return true;
};
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/Formula.java b/src/main/java/com/petrolpark/destroy/chemistry/Formula.java
index 420104182..47be267fa 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/Formula.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/Formula.java
@@ -274,7 +274,7 @@ public static Formula joinFormulae(Formula formula1, Formula formula2, BondType
* @see Formula#startingAtom How sub-Formulae are added
* @see Formula#joinFormulae Connecting Formulae of which you are unaware of the specific structure of each Formula, such as in {@link GenericReaction Generic Reactions}
*/
- public Formula addGroup(Formula group, Boolean isSideGroup, BondType bondType) {
+ public Formula addGroup(Formula group, boolean isSideGroup, BondType bondType) {
if (topology.atomsAndLocations.stream().anyMatch(pair -> pair.getSecond() == currentAtom)) {
throw new FormulaModificationException(this, "Cannot modify Atoms in cycle");
};
@@ -492,8 +492,10 @@ public void updateSideChainStructures() {
if (structure.get(atom) == null) continue checkAllAtomsInSideChain; // If this Atom has no Bonds, don't do anything
addAllBondsForAtom: for (Bond bond : structure.get(atom)) {
Atom potentialNewAtom = bond.getDestinationAtom();
- if (topology.formula.structure.keySet().contains(potentialNewAtom)) continue addAllBondsForAtom; // Don't add Bonds to Atoms which are part of the Topology (and therefore not part of the side branch)
- if (!sideChainFormula.structure.keySet().contains(potentialNewAtom)) newSideChainFormula.structure.put(potentialNewAtom, structure.get(potentialNewAtom)); // Add any as-of-yet unknown Atoms to the side branch's structure
+ if (topology.formula.structure.containsKey(potentialNewAtom)) continue addAllBondsForAtom; // Don't add Bonds to Atoms which are part of the Topology (and therefore not part of the side branch)
+ if (!sideChainFormula.structure.containsKey(potentialNewAtom)) {
+ newSideChainFormula.structure.put(potentialNewAtom, structure.get(potentialNewAtom)); // Add any as-of-yet unknown Atoms to the side branch's structure
+ }
bonds.add(bond);
};
newSideChainFormula.structure.put(atom, bonds);
@@ -746,14 +748,14 @@ public Formula shallowCopy() {
* @param isCarbanion
* @see Molecule#getCarbocationStability The wrapper for this Method
*/
- public Float getCarbocationStability(Atom carbon, boolean isCarbanion) {
- Float totalElectronegativity = 0f;
+ public float getCarbocationStability(Atom carbon, boolean isCarbanion) {
+ float totalElectronegativity = 0f;
for (Bond bond : structure.get(carbon)) {
totalElectronegativity += bond.getDestinationAtom().getElement().getElectronegativity() * bond.getType().getEquivalent();
};
- Float relativeElectronegativity = totalElectronegativity - (Element.CARBON.getElectronegativity() * 4);
- Float relativeStability = 1f + ((float)Math.pow(relativeElectronegativity, 4) / (float)Math.abs(relativeElectronegativity));
- return isCarbanion ^ relativeElectronegativity < 0 ? 1f / (Float)relativeStability : (Float)relativeStability;
+ float relativeElectronegativity = totalElectronegativity - (Element.CARBON.getElectronegativity() * 4);
+ float relativeStability = 1f + ((float)Math.pow(relativeElectronegativity, 4) / Math.abs(relativeElectronegativity));
+ return isCarbanion ^ relativeElectronegativity < 0 ? 1f / relativeStability : relativeStability;
};
/**
@@ -772,14 +774,15 @@ public Branch getRenderBranch() {
*/
private static Map> shallowCopyStructure(Map> structureToCopy) {
Map> newStructure = new HashMap<>();
- for (Atom atom : structureToCopy.keySet()) {
- List oldBonds = structureToCopy.get(atom);
- List newBonds = new ArrayList<>();
+ for(Entry> moleculeEntry : structureToCopy.entrySet()) {
+ Atom atom = moleculeEntry.getKey();
+ List oldBonds = moleculeEntry.getValue();
+ List newBonds = new ArrayList<>(oldBonds.size());
for (Bond oldBond : oldBonds) {
newBonds.add(new Bond(atom, oldBond.getDestinationAtom(), oldBond.getType()));
};
newStructure.put(atom, newBonds);
- };
+ }
return newStructure;
};
@@ -801,7 +804,7 @@ private static Branch getMaximumBranch(Atom startAtom, Map> str
Branch maximumBranch = new Branch(currentNode);
- Boolean nodesAdded = true;
+ boolean nodesAdded = true;
while (nodesAdded) {
nodesAdded = false;
Map connectedUnvisitedNodesAndTheirBondTypes = new HashMap<>();
@@ -813,16 +816,17 @@ private static Branch getMaximumBranch(Atom startAtom, Map> str
};
if (connectedUnvisitedNodesAndTheirBondTypes.size() == 1) {
- Node onlyNode = connectedUnvisitedNodesAndTheirBondTypes.keySet().iterator().next();
- maximumBranch.add(onlyNode, connectedUnvisitedNodesAndTheirBondTypes.get(onlyNode));
+ Entry nodeEntry = connectedUnvisitedNodesAndTheirBondTypes.entrySet().iterator().next();
+ Node onlyNode = nodeEntry.getKey();
+ maximumBranch.add(onlyNode, nodeEntry.getValue());
currentNode = onlyNode;
nodesAdded = true;
- } else if (connectedUnvisitedNodesAndTheirBondTypes.size() != 0) {
+ } else if (!connectedUnvisitedNodesAndTheirBondTypes.isEmpty()) {
Map connectedBranchesAndTheirBondTypes = new HashMap<>();
- for (Node node : connectedUnvisitedNodesAndTheirBondTypes.keySet()) {
-
+ for(Entry nodeBondEntry : connectedUnvisitedNodesAndTheirBondTypes.entrySet()) {
+ Node node = nodeBondEntry.getKey();
Map> newStructure = shallowCopyStructure(structure); // Create a new Structure which does not include the current Node
Bond bondToRemove = null;
for (Bond bond : structure.get(node.getAtom())) {
@@ -836,15 +840,13 @@ private static Branch getMaximumBranch(Atom startAtom, Map> str
};
newStructure.remove(currentNode.getAtom());
-
+
Branch branch = getMaximumBranch(node.getAtom(), newStructure);
- connectedBranchesAndTheirBondTypes.put(branch, connectedUnvisitedNodesAndTheirBondTypes.get(node));
- };
+ connectedBranchesAndTheirBondTypes.put(branch, nodeBondEntry.getValue());
+ }
List orderedConnectedBranches = new ArrayList<>(connectedBranchesAndTheirBondTypes.keySet());
- Collections.sort(orderedConnectedBranches, (b1, b2) -> {
- return b2.getMass().compareTo(b1.getMass());
- });
+ orderedConnectedBranches.sort((b1, b2) -> b2.getMass().compareTo(b1.getMass()));
Branch biggestBranch = orderedConnectedBranches.get(0);
maximumBranch.add(biggestBranch, connectedBranchesAndTheirBondTypes.get(biggestBranch));
@@ -854,10 +856,7 @@ private static Branch getMaximumBranch(Atom startAtom, Map> str
for (Branch sideBranch : orderedConnectedBranches) {
currentNode.addSideBranch(sideBranch, connectedBranchesAndTheirBondTypes.get(sideBranch));
};
-
- } else {
-
- };
+ }
};
return maximumBranch;
@@ -920,7 +919,7 @@ private static void addBondBetweenAtoms(Map> structureToMutate,
private static Formula groupFromString(List symbols) {
Formula formula = Formula.nothing();
- Boolean hasFormulaBeenInstantiated = false;
+ boolean hasFormulaBeenInstantiated = false;
BondType nextAtomBond = BondType.SINGLE;
@@ -943,7 +942,7 @@ private static Formula groupFromString(List symbols) {
List subSymbols = new ArrayList<>();
while (brackets > 0) { //Keep going until the closing bracket is found
i++; //move to next Atom
- Boolean added = false;
+ boolean added = false;
for (int j = 0; j < symbols.get(i).length(); j++) {
char c = symbols.get(i).charAt(j);
if (c == ')') {
@@ -968,7 +967,7 @@ private static Formula groupFromString(List symbols) {
symbol = symbols.get(i);
};
- Boolean stripBond = true; //start by assuming by a =/#/~ will have to be taken off the end of the Symbol
+ boolean stripBond = true; //start by assuming by a =/#/~ will have to be taken off the end of the Symbol
nextAtomBond = BondType.SINGLE; //start by assuming the next Bond will be single
switch (symbol.charAt(symbol.length() - 1)) {
case '=':
@@ -1003,9 +1002,9 @@ private static Formula groupFromString(List symbols) {
hasFormulaBeenInstantiated = true;
};
- for (Formula group : groupsToAdd.keySet()) { //add all side Groups to the current Atom
- formula.addGroup(group, true, groupsToAdd.get(group));
- };
+ for(Entry groupEntry : groupsToAdd.entrySet()) {
+ formula.addGroup(groupEntry.getKey(), true, groupEntry.getValue());
+ }
i++; //move to the next Atom
};
@@ -1028,17 +1027,17 @@ private static BondType trailingBondType(String symbol) {
*/
private static Map> stripHydrogens(Map> structure) {
Map> newStructure = new HashMap<>();
- for (Atom atom : structure.keySet()) {
- List bondsToAdd = new ArrayList<>();
- for (Bond bond : structure.get(atom)) {
- if (!bond.getDestinationAtom().isHydrogen()) {
- bondsToAdd.add(bond);
- };
- };
- if (!atom.isHydrogen()) {
- newStructure.put(atom, bondsToAdd);
- };
- };
+ for(Entry> moleculeEntry : structure.entrySet()) {
+ Atom atom = moleculeEntry.getKey();
+ if(atom.isHydrogen()) continue;
+ List bondStructure = moleculeEntry.getValue();
+ List bondsToAdd = new ArrayList<>(bondStructure.size());
+ for(Bond bond : bondStructure) {
+ if(bond.getDestinationAtom().isHydrogen()) continue;
+ bondsToAdd.add(bond);
+ }
+ newStructure.put(atom, bondsToAdd);
+ }
return newStructure;
};
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/Group.java b/src/main/java/com/petrolpark/destroy/chemistry/Group.java
index c93df9b10..f9f45dc9b 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/Group.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/Group.java
@@ -29,7 +29,7 @@ public Group() {
* Get the {@link GenericReaction Generic Reactions} in which the given functional Group participates.
* @param group
*/
- public static final Set getReactionsOf(Group> group) {
+ public static Set getReactionsOf(Group> group) {
return groupTypesAndReactions.get(group.getType());
};
@@ -37,9 +37,9 @@ public static final Set getReactionsOf(Group> group) {
/**
* Get the {@link GenericReaction Generic Reactions} which the functional Group identified by the given ID participates in.
- * @param ID The {@link Group#getID String ID} of the functional Group.
+ * @param type ID of the group
*/
- public static final Set getReactionsOfGroupByID(GroupType> type) {
+ public static Set getReactionsOfGroupByID(GroupType> type) {
return groupTypesAndReactions.get(type);
};
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/IItemReactant.java b/src/main/java/com/petrolpark/destroy/chemistry/IItemReactant.java
index 20fc8f891..99acd1158 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/IItemReactant.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/IItemReactant.java
@@ -16,7 +16,7 @@ public interface IItemReactant {
* This should return {@code true} if the Item Stack is a valid reactant for this {@link Reaction}.
* @param stack
*/
- public boolean isItemValid(ItemStack stack);
+ boolean isItemValid(ItemStack stack);
/**
* Deal with 'consuming' this Item Stack - usually, this will involve shrinking the Stack by one,
@@ -24,7 +24,7 @@ public interface IItemReactant {
* untouched. If this is a {@link IItemReactant#isCatalyst catalyst}, this method is not called.
* @param stack
*/
- public default void consume(ItemStack stack) {
+ default void consume(ItemStack stack) {
stack.shrink(1);
};
@@ -32,17 +32,17 @@ public default void consume(ItemStack stack) {
* Get the List of Item Stacks through which the JEI slot should cycle. For Tag Reactants for
* example, this should be a List of all Item Stacks in that Tag.
*/
- public List getDisplayedItemStacks();
+ List getDisplayedItemStacks();
/**
* Whether this Item Reactant is a catalyst. If so, it will not be consumed, but just required
* for the {@link Reaction} to occur.
*/
- public default boolean isCatalyst() {
+ default boolean isCatalyst() {
return false;
};
- public class SimpleItemReactant implements IItemReactant {
+ class SimpleItemReactant implements IItemReactant {
protected final Supplier- item;
@@ -62,7 +62,7 @@ public List getDisplayedItemStacks() {
};
- public class SimpleItemCatalyst extends SimpleItemReactant {
+ class SimpleItemCatalyst extends SimpleItemReactant {
public SimpleItemCatalyst(Supplier
- item) {
super(item);
@@ -78,7 +78,7 @@ public boolean isCatalyst() {
};
- public class SimpleItemTagReactant implements IItemReactant {
+ class SimpleItemTagReactant implements IItemReactant {
protected final TagKey
- tag;
@@ -101,7 +101,7 @@ public List getDisplayedItemStacks() {
};
- public class SimpleItemTagCatalyst extends SimpleItemTagReactant {
+ class SimpleItemTagCatalyst extends SimpleItemTagReactant {
public SimpleItemTagCatalyst(TagKey
- tag) {
super(tag);
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/Mixture.java b/src/main/java/com/petrolpark/destroy/chemistry/Mixture.java
index 37e3b8f8a..6164b251c 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/Mixture.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/Mixture.java
@@ -52,7 +52,7 @@ public class Mixture extends ReadOnlyMixture {
/**`
* Every {@link Molecule} in this Mixture that has a {@link Group functional Group}, indexed by the {@link Group#getType Type} of that Group.
- * Molecules are stored as {@link com.petrolpark.destroy.chemistry.genericReaction.GenericReactant Generic Reactants}.
+ * Molecules are stored as {@link com.petrolpark.destroy.chemistry.genericreaction.GenericReactant Generic Reactants}.
* Molecules which have multiple of the same Group are indexed for each occurence of the Group.
*/
protected Map, List>> groupIDsAndMolecules;
@@ -172,7 +172,7 @@ public CompoundTag writeNBT() {
if (!reactionResults.isEmpty()) {
tag.put("Results", NBTHelper.writeCompoundList(reactionResults.entrySet(), entry -> {
CompoundTag resultTag = new CompoundTag();
- resultTag.putString("Result", entry.getKey().getReaction().getFullId());
+ resultTag.putString("Result", entry.getKey().getReaction().getFullID());
resultTag.putFloat("MolesPerBucket", entry.getValue());
return resultTag;
}));
@@ -230,7 +230,7 @@ public List getContents(boolean excludeNovel) {
* @return A new Mixture instance
*/
public static Mixture mix(Map mixtures) {
- if (mixtures.size() == 0) return new Mixture();
+ if (mixtures.isEmpty()) return new Mixture();
if (mixtures.size() == 1) return mixtures.keySet().iterator().next();
Mixture resultMixture = new Mixture();
Map moleculesAndMoles = new HashMap<>(); // A Map of all Molecules to their quantity in moles (not their concentration)
@@ -247,12 +247,12 @@ public static Mixture mix(Map mixtures) {
Molecule molecule = entry.getKey();
float concentration = entry.getValue();
moleculesAndMoles.merge(molecule, concentration * amount, (m1, m2) -> m1 + m2); // Add the Molecule to the map if it's a new one, or increase the existing molar quantity otherwise
- totalEnergy += molecule.getMolarHeatCapacity() * concentration * mixture.temperature * amount; // Add all the energy that would be required to raise this Molecule from 0K to its current temperature
- totalEnergy += molecule.getLatentHeat() * concentration * mixture.states.get(molecule) * amount; // Add all the energy that would be required to vaporise this Molecule, if necessary
+ totalEnergy += (float) (molecule.getMolarHeatCapacity() * concentration * mixture.temperature * amount); // Add all the energy that would be required to raise this Molecule from 0K to its current temperature
+ totalEnergy += (float) (molecule.getLatentHeat() * concentration * mixture.states.get(molecule) * amount); // Add all the energy that would be required to vaporise this Molecule, if necessary
};
for (Entry entry : mixture.reactionResults.entrySet()) {
- reactionResultsAndMoles.merge(entry.getKey(), entry.getValue() * amount, (r1, r2) -> r1 + r2); // Same for Reaction Results
+ reactionResultsAndMoles.merge(entry.getKey(), entry.getValue() * amount, Double::sum); // Same for Reaction Results
};
};
@@ -359,12 +359,11 @@ public void reactForTick(ReactionContext context, int cycles) {
};
// Check now if we have actually reached equilibrium or if that was a false assumption at the start
- for (Molecule molecule : oldContents.keySet()) {
- if (!areVeryClose(oldContents.get(molecule), getConcentrationOf(molecule))) { // If there's something that has changed concentration noticeably in this tick...
- equilibrium = false; // ...we cannot have reached equilibrium
- };
- };
-
+ for(Entry moleculeEntry : oldContents.entrySet()) {
+ if(areVeryClose(moleculeEntry.getValue(), getConcentrationOf(moleculeEntry.getKey()))) continue;
+ equilibrium = false;
+ break;
+ }
if (shouldRefreshPossibleReactions) { // If we added a new Molecule at any point
refreshPossibleReactions();
};
@@ -393,7 +392,7 @@ public void reactForTick(ReactionContext context, int cycles) {
/**
* Add or take heat from this Mixture. This will boil/condense Molecules and change the temperature.
- * @param energy In joules per bucket
+ * @param energyDensity In joules per bucket
*/
public void heat(float energyDensity) {
float volumetricHeatCapacity = getVolumetricHeatCapacity();
@@ -421,7 +420,7 @@ public void heat(float energyDensity) {
heat(energyDensity - energyRequiredToFullyBoil); // Continue heating
} else { // If there is no leftover energy and the Molecule is still boiling
float boiled = energyDensity / (molecule.getLatentHeat() * getConcentrationOf(molecule)); // The proportion of all of the Molecule which is additionally boiled
- states.merge(molecule, boiled, (f1, f2) -> f1 + f2);
+ states.merge(molecule, boiled, Float::sum);
};
equilibrium = false; // Equilibrium is broken when a Molecule evaporates
@@ -460,9 +459,9 @@ public void heat(float energyDensity) {
};
/**
- * Enact all {@link Reactions} that {@link Reaction#getItemReactants involve Item Stacks}. This does not just
+ * Enact all {@link Reaction}s that {@link Reaction#getItemReactants involve Item Stacks}. This does not just
* include dissolutions, but Item-catalyzed Reactions too.
- * @param availableStacks The Item Stacks available to this Mixture. This Stacks in this List will be modified
+ * @param context Context of the reaction, contains the Item Stacks available to this Mixture. This Stacks in this List will be modified
* @param volume The amount of this Mixture there is, in buckets
*/
public void dissolveItems(ReactionContext context, double volume) {
@@ -479,8 +478,7 @@ public void dissolveItems(ReactionContext context, double volume) {
if (orderedReactions.isEmpty()) return; // Don't go any further if there aren't any items to dissolve
- Collections.sort(possibleReactions, (r1, r2) -> ((Float)calculateReactionRate(r1, context)).compareTo(calculateReactionRate(r2, context))); // Order the list of Item-consuming Reactions by rate, in case multiple of them want the same Item
-
+ possibleReactions.sort((r1, r2) -> Float.compare(calculateReactionRate(r1, context), calculateReactionRate(r2, context))); // Order the list of Item-consuming Reactions by rate, in case multiple of them want the same Item
tryEachReaction: for (Reaction reaction : orderedReactions) {
/*
@@ -509,15 +507,13 @@ public void dissolveItems(ReactionContext context, double volume) {
for (IItemReactant itemReactant : reaction.getItemReactants()) {
boolean validItemFound = false; // Start by assuming we haven't yet come across the right Stack
- for (ItemStack stackCopy : copiesAndStacks.keySet()) {
- if (itemReactant.isItemValid(stackCopy)) {
- validItemFound = true; // We have now found the right Stack
- if (!itemReactant.isCatalyst()) { // If this Item gets used up
- itemReactant.consume(stackCopy); // Consume the Stack copy so we know for future simulations that it can't be used
- reactantsAndStacks.put(itemReactant, copiesAndStacks.get(stackCopy)); // Store the actual Item Stack to be consumed later
- };
- };
- };
+ for(Entry stackToCopy : copiesAndStacks.entrySet()) {
+ ItemStack stackCopy = stackToCopy.getKey();
+ if(!itemReactant.isItemValid(stackCopy)) continue;
+ validItemFound = true;
+ itemReactant.consume(stackCopy);
+ reactantsAndStacks.put(itemReactant, stackToCopy.getValue());
+ }
if (!validItemFound) continue tryEachReaction; // If the simulation was a failure, move onto the next Reaction.
};
@@ -581,7 +577,7 @@ public void scale(float volumeIncreaseFactor) {
reactionResults.replaceAll((reactionResult, molesPerBucket) -> molesPerBucket / volumeIncreaseFactor);
};
- public static record Phases(Mixture gasMixture, Double gasVolume, Mixture liquidMixture, Double liquidVolume) {};
+ public record Phases(Mixture gasMixture, Double gasVolume, Mixture liquidMixture, Double liquidVolume) {};
/**
* Get two new Mixtures from one - one containing all gas, one containing all liquid.
@@ -734,14 +730,13 @@ public ReactionInBasinResult reactInBasin(int volume, List availableS
public Map getCompletedResults(double volumeInBuckets) {
Map results = new HashMap<>();
if (reactionResults.isEmpty()) return results;
- for (ReactionResult result : reactionResults.keySet()) {
-
- if (result.isOneOff()) {
+ for(Entry reactionEntry : reactionResults.entrySet()) {
+ ReactionResult result = reactionEntry.getKey();
+ if(result.isOneOff()) {
results.put(result, 1);
continue;
- };
-
- Float molesPerBucketOfReaction = reactionResults.get(result);
+ }
+ float molesPerBucketOfReaction = reactionEntry.getValue();
int numberOfResult = (int) (volumeInBuckets * molesPerBucketOfReaction / result.getRequiredMoles());
if (numberOfResult == 0) continue;
@@ -749,7 +744,7 @@ public Map getCompletedResults(double volumeInBuckets)
reactionResults.replace(result, molesPerBucketOfReaction - numberOfResult * result.getRequiredMoles() / (float)volumeInBuckets);
results.put(result, numberOfResult);
- };
+ }
// reactionResults.keySet().removeIf(result -> { // Remove any one-off Results and Results which have run out
// return result.isOneOff() || areVeryClose(reactionResults.get(result), 0f);
// });
@@ -810,7 +805,7 @@ private boolean internalAddMolecule(Molecule molecule, float concentration, bool
if (!molecule.isNovel()) super.addMolecule(molecule, concentration);
List> functionalGroups = molecule.getFunctionalGroups();
- if (functionalGroups.size() != 0) {
+ if (!functionalGroups.isEmpty()) {
for (Group group : functionalGroups) { // Unparameterised raw type
addGroupToMixture(molecule, group); // Unchecked conversion
};
@@ -843,10 +838,8 @@ private boolean internalAddMolecule(Molecule molecule, float concentration, bool
private > void addGroupToMixture(Molecule molecule, G group) {
GroupType groupType = group.getType();
- if (!groupIDsAndMolecules.containsKey(groupType)) {
- groupIDsAndMolecules.put(groupType, new ArrayList<>());
- };
- groupIDsAndMolecules.get(groupType).add(new GenericReactant<>(molecule, group));
+ groupIDsAndMolecules.computeIfAbsent(groupType, g -> new ArrayList<>())
+ .add(new GenericReactant<>(molecule, group));
};
/**
@@ -882,7 +875,7 @@ private Mixture removeMolecule(Molecule molecule) {
* @param shouldRefreshReactions Whether to alter the possible {@link Reaction Reactions} in the case that a new Molecule is added to the Mixture (should almost always be {@code true})
*/
private Mixture changeConcentrationOf(Molecule molecule, float change, boolean shouldRefreshReactions) {
- Float currentConcentration = getConcentrationOf(molecule);
+ float currentConcentration = getConcentrationOf(molecule);
if (!contents.containsKey(molecule) && change > 0f) internalAddMolecule(molecule, change, shouldRefreshReactions);
@@ -901,60 +894,60 @@ private Mixture changeConcentrationOf(Molecule molecule, float change, boolean s
*/
private float calculateReactionRate(Reaction reaction, ReactionContext context) {
float rate = reaction.getRateConstant(temperature) / (float) TICKS_PER_SECOND;
- for (Molecule molecule : reaction.getOrders().keySet()) {
- rate *= (float)Math.pow(getConcentrationOf(molecule), reaction.getOrders().get(molecule));
- };
+ for(Entry moleculeEntry : reaction.getOrders().entrySet()) {
+ rate *= (float) Math.pow(getConcentrationOf(moleculeEntry.getKey()), moleculeEntry.getValue());
+ }
if (reaction.needsUV()) rate *= context.UVPower;
return rate;
};
/**
- * Determine all {@link Reaction Reactions} - including {@link GenericReactions Generic Reactions} that are possible with the {@link Molecule Molecules} in this Mixture,
+ * Determine all {@link Reaction Reactions} - including {@link GenericReaction Generic Reactions} that are possible with the {@link Molecule Molecules} in this Mixture,
* and update the {@link Mixture#possibleReactions stored possible Reactions} accordingly.
* This should be called whenever new Molecules have been {@link Mixture#addMolecule added} to the Mixture, or a Molecule has been removed entirely, but rarely otherwise.
*/
private void refreshPossibleReactions() {
- possibleReactions = new ArrayList<>();
+ ArrayList possibleReactions = new ArrayList<>();
Set newPossibleReactions = new HashSet<>();
// Generate specific Generic Reactions
- for (GroupType> groupType : groupIDsAndMolecules.keySet()) { // Only search for Generic Reactions of Groups present in this Molecule
- checkEachGenericReaction: for (GenericReaction genericReaction : Group.getReactionsOfGroupByID(groupType)) {
-
- if (genericReaction.involvesSingleGroup()) { // Generic Reactions involving only one functional Group
- newPossibleReactions.addAll(specifySingleGroupGenericReactions(genericReaction, groupIDsAndMolecules.get(groupType)));
-
- } else { // Generic Reactions involving two functional Groups
- if (!(genericReaction instanceof DoubleGroupGenericReaction, ?> dggr)) continue checkEachGenericReaction; // This check should never fail
- if (groupType != dggr.getFirstGroupType()) continue checkEachGenericReaction; // Only generate Reactions when we're dealing with the first Group type
-
- GroupType> secondGroupType = dggr.getSecondGroupType();
- if (!groupIDsAndMolecules.keySet().contains(secondGroupType)) continue checkEachGenericReaction; // We can't do this generic reaction if we only have one group type
-
- List, GenericReactant>>> reactantPairs = new ArrayList<>();
- for (GenericReactant> firstGenericReactant : groupIDsAndMolecules.get(groupType)) {
- for (GenericReactant> secondGenericReactant : groupIDsAndMolecules.get(secondGroupType)) {
- reactantPairs.add(Pair.of(firstGenericReactant, secondGenericReactant));
- };
+ for(Entry, List>> reactionEntry : groupIDsAndMolecules.entrySet()) {
+ GroupType> groupType = reactionEntry.getKey();
+ List> reactants = reactionEntry.getValue();
+ for(GenericReaction genericReaction : Group.getReactionsOfGroupByID(groupType)) {
+ if(genericReaction.involvesSingleGroup()) {
+ newPossibleReactions.addAll(specifySingleGroupGenericReactions(genericReaction, reactants));
+ continue;
+ }
+
+ if (!(genericReaction instanceof DoubleGroupGenericReaction, ?> dggr)) continue; // This check should never fail
+ if (groupType != dggr.getFirstGroupType()) continue; // Only generate Reactions when we're dealing with the first Group type
+
+ GroupType> secondGroupType = dggr.getSecondGroupType();
+ List> secondGroupReactants = groupIDsAndMolecules.get(secondGroupType);
+ if (secondGroupReactants == null) continue; // We can't do this generic reaction if we only have one group type
+
+ List, GenericReactant>>> reactantPairs = new ArrayList<>();
+ for (GenericReactant> firstGenericReactant : groupIDsAndMolecules.get(groupType)) {
+ for (GenericReactant> secondGenericReactant : secondGroupReactants) {
+ reactantPairs.add(Pair.of(firstGenericReactant, secondGenericReactant));
};
-
- newPossibleReactions.addAll(specifyDoubleGroupGenericReactions(dggr, reactantPairs));
};
- };
- };
+
+ newPossibleReactions.addAll(specifyDoubleGroupGenericReactions(dggr, reactantPairs));
+ }
+ }
//All Reactions
for (Molecule possibleReactant : contents.keySet()) {
newPossibleReactions.addAll(possibleReactant.getReactantReactions());
};
for (Reaction reaction : newPossibleReactions) {
- //possibleReactions.add(reaction);
-
/*
* This checks if all necessary Reactants were present before proceeding, however this leads to some infinite loops
* where one half of a reversible Reaction would happen one tick, then the other one the next, etc.
*/
- Boolean reactionHasAllReactants = true;
+ boolean reactionHasAllReactants = true;
for (Molecule necessaryReactantOrCatalyst : reaction.getOrders().keySet()) {
if (getConcentrationOf(necessaryReactantOrCatalyst) == 0) {
reactionHasAllReactants = false;
@@ -966,6 +959,8 @@ private void refreshPossibleReactions() {
};
};
+ this.possibleReactions = possibleReactions;
+
};
/**
@@ -974,8 +969,8 @@ private void refreshPossibleReactions() {
*
*
For example, if the Generic Reaction supplied is the {@link com.petrolpark.destroy.chemistry.index.genericreaction.AlkeneHydrolysis hydration of an alkene},
* and reactants includes {@code destroy:ethene}, the returned collection will include a Reaction with {@code destroy:ethene} and {@code destroy:water} as reactants,
- * {@code destroy:ethanol} as a product, and all the appropriate rate constants and catalysts as defined in the {@link com.petrolpark.destroy.chemistry.index.AlkeneHydrolysis.AlkeneHydration#generateReaction generator}.
- *
+ * {@code destroy:ethanol} as a product, and all the appropriate rate constants and catalysts as defined in the {@link com.petrolpark.destroy.chemistry.index.genericreaction.AlkeneHydrolysis#generateReaction generator}.
+ *
* @param G The Group to which this Generic Reaction applies
* @param genericReaction
* @param reactants All {@link GenericReactant Reactants} that have the Group
@@ -1027,7 +1022,7 @@ private , G2 extends Group> List specifyDoubl
return reactions;
};
- public static boolean areVeryClose(Float f1, Float f2) {
+ public static boolean areVeryClose(float f1, float f2) {
return Math.abs(f1 - f2) <= 0.0001f;
};
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/Molecule.java b/src/main/java/com/petrolpark/destroy/chemistry/Molecule.java
index b451b9d0e..8b4201f2b 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/Molecule.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/Molecule.java
@@ -1,11 +1,6 @@
package com.petrolpark.destroy.chemistry;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
+import java.util.function.Consumer;
import javax.annotation.Nullable;
@@ -57,9 +52,9 @@ public class Molecule implements INameableProduct {
/**
* The name space of the mod by which this Molecule was defined.
*/
- public final String nameSpace;
+ public final String namespace;
/**
- * The {@link Molecule#getFullID ID} of this Molecule, not including its {@link Molecule#nameSpace name space}.
+ * The {@link Molecule#getFullID ID} of this Molecule, not including its {@link Molecule#namespace name space}.
*/
private String id;
@@ -105,16 +100,16 @@ public class Molecule implements INameableProduct {
/**
* The {@link MoleculeTag tags} which apply to this Molecule.
*/
- private Set tags;
+ private final Set tags;
/**
* The specific {@link Reaction Reactions} in which this Molecule is a {@link Reaction#getReactants reactant}.
*/
- private List reactantReactions;
+ private final List reactantReactions;
/**
* The specific {@link Reaction Reactions} in which this Molecule is a {@link Reaction#getProducts product}.
*/
- private List productReactions;
+ private final List productReactions;
// DISPLAY PROPERTIES
@@ -144,7 +139,7 @@ public class Molecule implements INameableProduct {
private MoleculeRenderer renderer;
private Molecule(String nameSpace) {
- this.nameSpace = nameSpace;
+ this.namespace = nameSpace;
id = null;
structure = null;
@@ -199,7 +194,7 @@ public String getFullID() {
*/
return structure.serialize();
} else {
- return nameSpace+":"+id;
+ return namespace +":"+id;
}
};
@@ -307,8 +302,8 @@ public Set getAtoms() {
};
/**
- * Whether this Molecule is {@link MoleculeTag tagged} as being {@link DestroyMolecules.Tags.HYPOTHETICAL hypothetical}.
- * This is typically any {@link Group#getExampleMolecule exemplar Molecule} for a {@link Group functional Group},
+ * Whether this Molecule is {@link MoleculeTag tagged} as being {@link DestroyMolecules.Tags#HYPOTHETICAL hypothetical}.
+ * This is typically any exemplar Molecule for a {@link Group functional Group},
* something than contains {@link Element#R_GROUP R-groups}, or a Molecule that can't actually exist.
*/
public boolean isHypothetical() {
@@ -317,7 +312,7 @@ public boolean isHypothetical() {
/**
* Get all the {@link MoleculeTag tags} this Molecule has.
- * @return
+ * @return Molecule tags
*/
public ImmutableSet getTags() {
return ImmutableSet.copyOf(tags);
@@ -337,7 +332,7 @@ public boolean hasTag(MoleculeTag tag) {
* @see Molecule What a novel Molecule is
*/
public boolean isNovel() {
- return this.nameSpace == "novel";
+ return Objects.equals(this.namespace, "novel");
};
/**
@@ -393,7 +388,7 @@ public String getStructuralFormula() {
* @param isCarbanion Whether this calculation should be inverted (to calculate the relative stability of a carbanion)
* @return A value typically from 0-216
*/
- public Float getCarbocationStability(Atom carbon, boolean isCarbanion) {
+ public float getCarbocationStability(Atom carbon, boolean isCarbanion) {
return structure.getCarbocationStability(carbon, isCarbanion);
};
@@ -415,6 +410,14 @@ public void addProductReaction(Reaction reaction) {
if (reaction.containsProduct(this)) productReactions.add(reaction);
};
+ public void removeProductReaction(Reaction reaction) {
+ productReactions.remove(reaction);
+ }
+
+ public void removeReactantReaction(Reaction reaction) {
+ reactantReactions.remove(reaction);
+ }
+
/**
* Get the list of {@link Reaction Reactions} of which this Molecule is a necessary Reactant.
* @return List of Reactions ordered by declaration
@@ -432,7 +435,7 @@ public List getProductReactions() {
};
/**
- * Get the {@link Molecule#name display name} of this Molecule.
+ * Get the display name of this Molecule.
* @param iupac Whether to use the IUPAC systematic name rather than the common one
*/
@Override
@@ -443,7 +446,7 @@ public Component getName(boolean iupac) {
public String getTranslationKey(boolean iupac) {
if (isNovel()) return "destroy.chemical.unknown";
- String key = nameSpace + ".chemical." + translationKey;
+ String key = namespace + ".chemical." + translationKey;
String iupacKey = key + ".iupac";
if (iupac && I18n.exists(iupacKey)) {
key = iupacKey;
@@ -539,11 +542,31 @@ public MoleculeRenderer getRenderer() {
return renderer;
};
+ public static void addMolecule(Molecule molecule) {
+ MOLECULES.put(molecule.getFullID(), molecule);
+ for(Reaction reaction : molecule.reactantReactions) {
+ Reaction.addReaction(reaction);
+ }
+ for(Reaction reaction : molecule.productReactions) {
+ Reaction.addReaction(reaction);
+ }
+ }
+
+ public static void removeMolecule(Molecule molecule) {
+ MOLECULES.remove(molecule.getFullID());
+ for(Reaction reaction : molecule.reactantReactions) {
+ Reaction.removeReaction(reaction);
+ }
+ for(Reaction reaction : molecule.productReactions) {
+ Reaction.removeReaction(reaction);
+ }
+ }
+
/**
* A class for constructing {@link Molecule Molecules}. This is typically used for:
* - Declaring known Molecules ({@link DestroyMolecules example})
* - Constructing novel Molecules for {@link com.petrolpark.destroy.chemistry.genericreaction.GenericReaction Generic Reactions}
- * ({@link com.petrolpark.destroy.chemistry.index.genericreaction.HydroxideSubstitutions#generateReaction example}).
+ * ({@link com.petrolpark.destroy.chemistry.index.genericreaction.AlcoholDehydration#generateReaction example}).
*
* Use {@code build()} to build get the Molecule.
*/
@@ -551,23 +574,23 @@ public static class MoleculeBuilder {
private Molecule molecule;
- private Boolean hasForcedDensity = false; // Whether this molecule has a custom density or it should be calculated
- private Boolean hasForcedBoilingPoint = false; // Whether this molecule has a custom boiling point or it should be calculated
- private Boolean hasForcedDipoleMoment = false; // Whether this molecule has a forced dipole moment or it should be calculated
- private Boolean hasForcedMolarHeatCapacity = false; // Whether this molecule has a forced specific heat capacity or it should be calculated
- private Boolean hasForcedLatentHeat = false; // Whether this molecule has a forced latent heat of fusion or it should be calculated
+ private boolean hasForcedDensity = false; // Whether this molecule has a custom density or it should be calculated
+ private boolean hasForcedBoilingPoint = false; // Whether this molecule has a custom boiling point or it should be calculated
+ private boolean hasForcedDipoleMoment = false; // Whether this molecule has a forced dipole moment or it should be calculated
+ private boolean hasForcedMolarHeatCapacity = false; // Whether this molecule has a forced specific heat capacity or it should be calculated
+ private boolean hasForcedLatentHeat = false; // Whether this molecule has a forced latent heat of fusion or it should be calculated
private String translationKey;
/**
* A {@link Molecule} constructor.
- * @param nameSpace The {@link Molecule#nameSpace name space} to which all Molecules constructed with this builder will belong
+ * @param nameSpace The {@link Molecule#namespace name space} to which all Molecules constructed with this builder will belong
* @throws IllegalArgumentException If a {@link Molecule#FORBIDDEN_NAMESPACES forbidden name space} is used, for example {@code novel}.
*/
public MoleculeBuilder(String nameSpace) {
molecule = new Molecule(nameSpace);
if (FORBIDDEN_NAMESPACES.contains(nameSpace)) {
- throw e("Cannot use name space '"+nameSpace+"'.");
+ error("Cannot use name space '" + nameSpace + "'.");
};
NAMESPACES.add(nameSpace);
molecule.charge = 0; //default
@@ -600,7 +623,8 @@ public MoleculeBuilder structure(Formula structure) {
molecule.structure = structure;
return this;
} catch (FormulaModificationException e) {
- throw e("Cannot use structure.", e);
+ error("Cannot use structure.", e);
+ return null;
}
};
@@ -630,7 +654,7 @@ public MoleculeBuilder charge(int charge) {
/**
* Set the {@link Molecule#boilingPoint boiling point} in degrees Celsius.
- * If not supplied, the boiling point will be very loosely {@link MoleculeBuilder#calculateBoilingPoint estimated}, but setting one is recommended.
+ * If not supplied, the boiling point will be very loosely given value, but setting one is recommended.
* @param boilingPoint In degrees Celcius
* @return This Molecule Builder
*/
@@ -640,7 +664,7 @@ public MoleculeBuilder boilingPoint(float boilingPoint) {
/**
* Set the {@link Molecule#boilingPoint boiling point} in kelvins.
- * If not supplied, the boiling point will be very loosely {@link MoleculeBuilder#calculateBoilingPoint estimated}, but setting one is recommended.
+ * If not supplied, the boiling point will be very loosely {@link MoleculeBuilder#boilingPoint estimated}, but setting one is recommended.
* @param boilingPoint In kelvins
* @return This Molecule Builder
*/
@@ -652,7 +676,7 @@ public MoleculeBuilder boilingPointInKelvins(float boilingPoint) {
/**
* Set the {@link Molecule#dipoleMoment dipole moment} of this Molecule.
- * If not supplied, a dipole moment will be very loosely {@link MoleculeBuilder#calculateDipoleMoment estimated}, but setting one is recommended.
+ * If not supplied, a dipole moment will be very loosely given value, but setting one is recommended.
* @return This Molecule Builder
*/
public MoleculeBuilder dipoleMoment(int dipoleMoment) {
@@ -674,11 +698,11 @@ public MoleculeBuilder specificHeatCapacity(float specificHeatCapacity) {
/**
* Set the {@link Molecule#molarHeatCapacity molar heat capacity} for this Molecule,
* in joules per mole-kelvin.
- * @param molarHeatCapacity
+ * @param molarHeatCapacity New capacity
* @return This Molecule Builder
*/
public MoleculeBuilder molarHeatCapacity(float molarHeatCapacity) {
- if (molarHeatCapacity <= 0f) throw e("Molar heat capacity must be greater than 0.");
+ if (molarHeatCapacity <= 0f) error("Molar heat capacity must be greater than 0.");
molecule.molarHeatCapacity = molarHeatCapacity;
hasForcedMolarHeatCapacity = true;
return this;
@@ -691,7 +715,7 @@ public MoleculeBuilder molarHeatCapacity(float molarHeatCapacity) {
* @return This Molecule Builder
*/
public MoleculeBuilder latentHeat(float latentHeat) {
- if (latentHeat <= 0f) throw e("Latent heat of fusion must be greater than 0.");
+ if (latentHeat <= 0f) error("Latent heat of fusion must be greater than 0.");
molecule.latentHeat = latentHeat;
hasForcedLatentHeat = true;
return this;
@@ -743,7 +767,7 @@ public MoleculeBuilder tag(MoleculeTag ...tags) {
* This is the only safe way to declare a Molecule.
* @return A new Molecule instance
* @throws IllegalArgumentException If the Molecule's {@link Formula structure} was not {@link MoleculeBuilder#structure declared},
- * or it is not novel and the {@link Molecule#nameSpace name space} was not declared.
+ * or it is not novel and the {@link Molecule#namespace name space} was not declared.
*/
public Molecule build() {
@@ -751,12 +775,12 @@ public Molecule build() {
molecule.translationKey = translationKey;
if (molecule.structure == null) {
- throw e("Molecule's structure has not been declared.");
+ error("Molecule's structure has not been declared.");
};
- if (molecule.getAtoms().size() >= 100) throw e("Molecule has too many Atoms");
-
- if (molecule.nameSpace == "novel") {
+ if (molecule.getAtoms().size() >= 100) error("Molecule has too many Atoms");
+ boolean isNovelNamespace = Objects.equals(molecule.namespace, "novel");
+ if (isNovelNamespace) {
Molecule equivalentMolecule = molecule.getEquivalent();
if (equivalentMolecule != molecule) {
return equivalentMolecule;
@@ -780,14 +804,13 @@ public Molecule build() {
molecule.refreshFunctionalGroups();
molecule.structure.updateSideChainStructures();
- if (molecule.nameSpace != "novel") {
+ if (!isNovelNamespace) {
if (molecule.id == null) {
- throw e("Molecule's ID has not been declared.");
+ error("Molecule's ID has not been declared.");
} else {
- MOLECULES.put(molecule.nameSpace+":"+molecule.id, molecule);
+ MOLECULES.put(molecule.getFullID(), molecule);
};
};
-
return molecule;
};
@@ -804,7 +827,7 @@ private float calculateMass() {
* Very loosely estimate the density of an organic molecule.
* @return A density in kilograms per metre cubed
*/
- private static final float estimateDensity(Molecule molecule) {
+ private static float estimateDensity(Molecule molecule) {
return 1000f; // Assume the density is similar to water, which is true for a lot of organic molecules.
};
@@ -835,7 +858,7 @@ private static int estimateDipoleMoment(Molecule molecule) {
return 0;
};
- public class MoleculeConstructionException extends ChemistryException {
+ public static class MoleculeConstructionException extends ChemistryException {
private MoleculeConstructionException(String message) {
super(message);
@@ -847,16 +870,16 @@ private MoleculeConstructionException(String message, Throwable e) {
};
- private MoleculeConstructionException e(String message) {
- return new MoleculeConstructionException(addInfoToMessage(message));
+ private void error(String message) {
+ throw new MoleculeConstructionException(addInfoToMessage(message));
};
- private MoleculeConstructionException e(String message, Throwable e) {
- return new MoleculeConstructionException(addInfoToMessage(message), e);
+ private void error(String message, Throwable e) {
+ throw new MoleculeConstructionException(addInfoToMessage(message), e);
};
private String addInfoToMessage(String message) {
- String id = molecule.id == null ? "Unknown ID" : molecule.nameSpace + ":" + molecule.id;
+ String id = molecule.id == null ? "Unknown ID" : molecule.namespace + ":" + molecule.id;
return "Problem building Molecule (" + id + "): " + message;
};
};
@@ -870,5 +893,9 @@ public String toString() {
return MoreObjects.toStringHelper(this).add("ID", getFullID()).toString();
};
+ @Override
+ public int hashCode() {
+ return getFullID().hashCode();
+ }
};
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/Reaction.java b/src/main/java/com/petrolpark/destroy/chemistry/Reaction.java
index abbad2f18..a2f3fa938 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/Reaction.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/Reaction.java
@@ -1,11 +1,6 @@
package com.petrolpark.destroy.chemistry;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
+import java.util.*;
import java.util.Map.Entry;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@@ -30,7 +25,7 @@ public class Reaction {
public static final Float GAS_CONSTANT = 8.3145f;
/**
- * The set of all Reactions known to Destroy, indexed by their {@link id IDs}.
+ * The set of all Reactions known to Destroy, indexed by their {@link Reaction#id IDs}.
*/
public static final Map REACTIONS = new HashMap<>();
@@ -78,9 +73,9 @@ public static ReactionBuilder generatedReactionBuilder() {
* The namespace of the mod by which this Reaction was declared, or {@code novel} if this was generated
* by a {@link com.petrolpark.destroy.chemistry.genericreaction.GenericReaction Reaction generator}.
*/
- private String nameSpace;
+ private final String namespace;
/**
- * The ID of this reaction, not including its {@link Reaction#nameSpace name space}, and {@code null} if this
+ * The ID of this reaction, not including its {@link Reaction#namespace name space}, and {@code null} if this
* Reaction was generated by a {@link com.petrolpark.destroy.chemistry.genericreaction.GenericReaction Reaction
* generator}.
* @see Reaction#getId The getter for this field
@@ -107,7 +102,7 @@ public static ReactionBuilder generatedReactionBuilder() {
private Reaction reverseReaction;
/**
- * Get the Reaction with the given {@link Reaction#getFullId ID}.
+ * Get the Reaction with the given {@link Reaction#getFullID ID}.
* @param reactionId In the format {@code :}
* @return {@code null} if no Reaction exists with that ID
*/
@@ -116,21 +111,21 @@ public static Reaction get(String reactionId) {
};
private Reaction(String nameSpace) {
- this.nameSpace = nameSpace;
+ this.namespace = nameSpace;
};
/**
* Whether this Molecule gets consumed in this Reaction (does not include catalysts).
*/
- public Boolean containsReactant(Molecule molecule) {
- return this.reactants.keySet().contains(molecule);
+ public boolean containsReactant(Molecule molecule) {
+ return this.reactants.containsKey(molecule);
};
/**
* Whether this Molecule is created in this Reaction.
*/
- public Boolean containsProduct(Molecule molecule) {
- return this.products.keySet().contains(molecule);
+ public boolean containsProduct(Molecule molecule) {
+ return this.products.containsKey(molecule);
};
/**
@@ -231,7 +226,7 @@ public ReactionResult getResult() {
* also acts as its translation key. {@code .reaction.} should hold
* the name of this Reaction, and {@code .reaction..description}
* should hold the description of this Reaction.
- * @see Reaction#getFullId Get the full ID
+ * @see Reaction#getFullID Get the full ID
*/
public String getId() {
return id;
@@ -241,8 +236,8 @@ public String getId() {
* Get the fully unique ID for this Reaction, in the format {@code :
* }, for example {@code destroy:chloroform_fluorination}.
*/
- public String getFullId() {
- return nameSpace + ":" + id;
+ public String getFullID() {
+ return namespace + ":" + id;
};
/**
@@ -273,8 +268,8 @@ public Optional getReverseReactionForDisplay() {
* The name space of the mod by which this Reaction was defined.
* @return {@code "novel"} if this was generated automatically by a {@link com.petrolpark.destroy.chemistry.genericreaction.GenericReaction Reaction generator}.
*/
- public String getNameSpace() {
- return nameSpace;
+ public String getNamespace() {
+ return namespace;
};
/**
@@ -282,12 +277,8 @@ public String getNameSpace() {
* @param reactant
* @return {@code 0} if this Molecule is not a reactant
*/
- public Integer getReactantMolarRatio(Molecule reactant) {
- if (!reactants.keySet().contains(reactant)) {
- return 0;
- } else {
- return reactants.get(reactant);
- }
+ public int getReactantMolarRatio(Molecule reactant) {
+ return reactants.getOrDefault(reactant, 0);
};
/**
@@ -295,12 +286,8 @@ public Integer getReactantMolarRatio(Molecule reactant) {
* @param product
* @return {@code 0} if this Molecule is not a product
*/
- public Integer getProductMolarRatio(Molecule product) {
- if (!products.keySet().contains(product)) {
- return 0;
- } else {
- return products.get(product);
- }
+ public int getProductMolarRatio(Molecule product) {
+ return products.getOrDefault(product, 0);
};
/**
@@ -358,7 +345,7 @@ public ReactionBuilder(String namespace) {
};
private void checkNull(Molecule molecule) {
- if (molecule == null) throw e("Molecules cannot be null");
+ if (molecule == null) error("Molecules cannot be null");
};
/**
@@ -407,7 +394,7 @@ public ReactionBuilder addReactant(Molecule molecule, int ratio, int order) {
* @see ReactionBuilder#addCatalyst(Molecule, int) Adding order with respect to a Molecule that is not a reactant (i.e. a catalyst)
*/
public ReactionBuilder setOrder(Molecule molecule, int order) {
- if (!reaction.reactants.keySet().contains(molecule)) throw e("Cannot modify order of a Molecule ("+ molecule.getFullID() +") that is not a reactant.");
+ if (!reaction.reactants.containsKey(molecule)) error("Cannot modify order of a Molecule ("+ molecule.getFullID() +") that is not a reactant.");
addCatalyst(molecule, order);
return this;
};
@@ -422,7 +409,7 @@ public ReactionBuilder setOrder(Molecule molecule, int order) {
* @see ReactionBuilder#addSimpleItemTagReactant Adding an Item Tag as a Reactant
*/
public ReactionBuilder addItemReactant(IItemReactant itemReactant, float moles) {
- if (reaction.molesPerItem != 0f && reaction.molesPerItem != moles) throw e("The number of moles of Reaction which occur when all Item Requirements are met is constant for a Reaction, not individual per Item Reactant. The same number must be supplied each time an Item Reactant is added.");
+ if (reaction.molesPerItem != 0f && reaction.molesPerItem != moles) error("The number of moles of Reaction which occur when all Item Requirements are met is constant for a Reaction, not individual per Item Reactant. The same number must be supplied each time an Item Reactant is added.");
reaction.molesPerItem = moles;
reaction.itemReactants.add(itemReactant);
return this;
@@ -589,7 +576,7 @@ public ReactionBuilder enthalpyChange(float enthalpyChange) {
* @return This Reaction Builder
*/
public ReactionBuilder withResult(float moles, BiFunction reactionresultFactory) {
- if (reaction.result != null) throw e("Reaction already has a Reaction Result. Use a CombinedReactionResult to have multiple.");
+ if (reaction.result != null) error("Reaction already has a Reaction Result. Use a CombinedReactionResult to have multiple.");
reaction.result = reactionresultFactory.apply(moles, reaction);
return this;
};
@@ -605,7 +592,7 @@ public ReactionBuilder withResult(float moles, BiFunction reverseReactionModifier) {
- if (generated) throw e("Generated Reactions cannot be reversible. Add another Generic Reaction instead.");
+ if (generated) error("Generated Reactions cannot be reversible. Add another Generic Reaction instead.");
reaction.displayAsReversible = true;
ReactionBuilder reverseBuilder = new ReactionBuilder(namespace);
for (Entry reactant : reaction.reactants.entrySet()) {
@@ -657,7 +644,7 @@ public ReactionBuilder reverseReaction(Consumer reverseReaction
reverseBuilder.addReactant(product.getKey(), product.getValue());
};
for (Entry rateAffecter : reaction.orders.entrySet()) {
- if (reaction.reactants.keySet().contains(rateAffecter.getKey())) continue; // Ignore reactants, only add catalysts
+ if (reaction.reactants.containsKey(rateAffecter.getKey())) continue; // Ignore reactants, only add catalysts
reverseBuilder.addCatalyst(rateAffecter.getKey(), rateAffecter.getValue());
};
reaction.reverseReaction = reverseBuilder.reaction; // Set this so JEI knows
@@ -680,7 +667,7 @@ public ReactionBuilder reverseReaction(Consumer reverseReaction
reaction.activationEnergy - reaction.enthalpyChange != reverseBuilder.reaction.activationEnergy
|| reaction.enthalpyChange != -reverseBuilder.reaction.enthalpyChange
) {
- throw e("Activation energies and enthalpy changes for reversible Reactions must obey Hess' Law");
+ error("Activation energies and enthalpy changes for reversible Reactions must obey Hess' Law");
};
reverseBuilder.build();
@@ -690,7 +677,7 @@ public ReactionBuilder reverseReaction(Consumer reverseReaction
public Reaction build() {
if (reaction.id == null && !generated) {
- throw e("Reaction is missing an ID.");
+ error("Reaction is missing an ID.");
};
if (!hasForcedActivationEnergy) {
@@ -709,19 +696,14 @@ public Reaction build() {
};
if (!generated) {
- for (Molecule reactant : reaction.reactants.keySet()) {
- reactant.addReactantReaction(reaction);
- };
- for (Molecule product : reaction.products.keySet()) {
- product.addProductReaction(reaction);
- };
- REACTIONS.put(reaction.getFullId(), reaction);
- };
+ registerReaction(reaction);
+ }
return reaction;
};
- public class ReactionConstructionException extends ChemistryException {
+
+ public static class ReactionConstructionException extends ChemistryException {
public ReactionConstructionException(String message) {
super(message);
@@ -729,24 +711,75 @@ public ReactionConstructionException(String message) {
};
- private ReactionConstructionException e(String message) {
- String id = reaction.id == null ? reactionString() : reaction.nameSpace + ":" + reaction.id;
- return new ReactionConstructionException("Problem generating reation ("+ id + "): " + message);
+ private void error(String message) {
+ String id = reaction.id == null ? reactionString() : reaction.namespace + ":" + reaction.id;
+ throw new ReactionConstructionException("Problem generating reaction (" + id + "): " + message);
};
private String reactionString() {
- String reactionString = "";
+ StringBuilder reactionString = new StringBuilder();
for (Molecule reactant : reaction.reactants.keySet()) {
- reactionString += reactant.getSerlializedMolecularFormula(false);
- reactionString += " + ";
+ reactionString.append(reactant.getSerlializedMolecularFormula(false));
+ reactionString.append(" + ");
};
- reactionString = reactionString.substring(0, reactionString.length() - 3) + " => ";
+ reactionString = new StringBuilder(reactionString.substring(0, reactionString.length() - 3) + " => ");
for (Molecule product : reaction.products.keySet()) {
- reactionString += product.getSerlializedMolecularFormula(false);
- reactionString += " + ";
+ reactionString.append(product.getSerlializedMolecularFormula(false));
+ reactionString.append(" + ");
};
- reactionString = reactionString.substring(0, reactionString.length() - 3);
- return reactionString;
+ reactionString = new StringBuilder(reactionString.substring(0, reactionString.length() - 3));
+ return reactionString.toString();
};
};
+
+ public static void registerReaction(Reaction reaction) {
+ if(reaction == null) {
+ Destroy.LOGGER.warn("Tried to register a null reaction");
+ return;
+ }
+ addReaction(reaction);
+ for(Molecule product : reaction.getProducts()) {
+ product.addProductReaction(reaction);
+ }
+
+ for(Molecule reactant : reaction.getReactants()) {
+ reactant.addReactantReaction(reaction);
+ }
+ }
+
+ public static void unregisterReaction(Reaction reaction) {
+ if(reaction == null) {
+ Destroy.LOGGER.warn("Tried to unregister a null reaction");
+ return;
+ }
+ removeReaction(reaction);
+ for(Molecule product : reaction.products.keySet()) {
+ product.removeProductReaction(reaction);
+ }
+
+ for(Molecule reactant : reaction.reactants.keySet()) {
+ reactant.removeReactantReaction(reaction);
+ }
+ }
+
+ /**
+ * Makes reaction visible to the rest of the code. Does nothing else
+ * @param reaction Reaction to make visible
+ */
+ public static void addReaction(Reaction reaction) {
+ REACTIONS.put(reaction.getFullID(), reaction);
+ }
+
+ /**
+ * Makes reaction not possible to do in a vat. Does nothing else
+ * @param reaction Reaction to make invisible
+ */
+ public static void removeReaction(Reaction reaction) {
+ REACTIONS.remove(reaction.getFullID());
+ }
+
+ @Override
+ public int hashCode() {
+ return getFullID().hashCode();
+ }
}
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/ReadOnlyMixture.java b/src/main/java/com/petrolpark/destroy/chemistry/ReadOnlyMixture.java
index 8865e6ee7..9c5b17c71 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/ReadOnlyMixture.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/ReadOnlyMixture.java
@@ -1,12 +1,8 @@
package com.petrolpark.destroy.chemistry;
import java.text.DecimalFormat;
-import java.util.ArrayList;
-import java.util.Collections;
+import java.util.*;
import java.util.function.Function;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Predicate;
import java.util.function.Supplier;
@@ -79,12 +75,14 @@ public class ReadOnlyMixture {
*/
protected Map states;
+ public static final float BASE_TEMPERATURE = 298f;
+
public ReadOnlyMixture() {
translationKey = "";
contents = new HashMap<>();
- temperature = 298f;
+ temperature = BASE_TEMPERATURE;
states = new HashMap<>();
};
@@ -94,7 +92,7 @@ public ReadOnlyMixture() {
*/
public CompoundTag writeNBT() {
CompoundTag compound = new CompoundTag();
- if (translationKey != "" && translationKey != null) {
+ if (!Objects.equals(translationKey, "") && translationKey != null) {
compound.putString("TranslationKey", translationKey);
};
compound.putFloat("Temperature", temperature);
@@ -180,11 +178,7 @@ public boolean isEmpty() {
* @return 0 if the Mixture does not contain the given Molecule
*/
public float getConcentrationOf(Molecule molecule) {
- if (contents.containsKey(molecule)) {
- return contents.get(molecule);
- } else {
- return 0f;
- }
+ return contents.getOrDefault(molecule, 0f);
};
/**
@@ -269,10 +263,10 @@ public List getContents(boolean excludeNovel) {
* (Used for debugging).
*/
public String getContentsString() {
- String string = "";
- if (contents.isEmpty()) return string;
+ StringBuilder string = new StringBuilder();
+ if (contents.isEmpty()) return string.toString();
for (Entry entry : contents.entrySet()) {
- string += entry.getKey().getFullID() + " (" + entry.getValue() + "M), ";
+ string.append(entry.getKey().getFullID()).append(" (").append(entry.getValue()).append("M), ");
};
return string.substring(0, string.length() - 2);
};
@@ -289,7 +283,7 @@ public List getContentsTooltip(boolean iupac, boolean monospace, bool
int i = 0;
List tooltip = new ArrayList<>();
List molecules = new ArrayList<>(contents.keySet());
- Collections.sort(molecules, (m1, m2) -> contents.get(m2).compareTo(contents.get(m1)));
+ molecules.sort((m1, m2) -> contents.get(m2).compareTo(contents.get(m1)));
Function quantityTranslator = q -> DestroyLang.translate(useMoles ? "tooltip.mixture_contents.moles" : "tooltip.mixture_contents.concentration", concentrationFormatter.format(q)).string();
int quantityLabelLength = quantityTranslator.apply(0f).length() + 2;
for (Molecule molecule : molecules) {
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/genericreaction/SingleGroupGenericReaction.java b/src/main/java/com/petrolpark/destroy/chemistry/genericreaction/SingleGroupGenericReaction.java
index 7d982a36d..6f1fe8460 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/genericreaction/SingleGroupGenericReaction.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/genericreaction/SingleGroupGenericReaction.java
@@ -1,5 +1,6 @@
package com.petrolpark.destroy.chemistry.genericreaction;
+import com.blamejared.crafttweaker.api.CraftTweakerAPI;
import com.petrolpark.destroy.Destroy;
import com.petrolpark.destroy.chemistry.Atom;
import com.petrolpark.destroy.chemistry.Element;
@@ -17,7 +18,7 @@ public abstract class SingleGroupGenericReaction> extends Gen
public SingleGroupGenericReaction(ResourceLocation id, GroupType type) {
super(id);
- this.type = type;;
+ this.type = type;
Group.groupTypesAndReactions.get(type).add(this);
GENERIC_REACTIONS.add(this); // Add this to the list of all known Generic Reactions
};
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/index/DestroyGroupFinder.java b/src/main/java/com/petrolpark/destroy/chemistry/index/DestroyGroupFinder.java
index 7fb2e67ce..1ee6b8d8b 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/index/DestroyGroupFinder.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/index/DestroyGroupFinder.java
@@ -32,6 +32,7 @@ public List> findGroups(Map> structure) {
List carbonsToIgnore = new ArrayList<>();
List carbonsToIgnoreForAlkenes = new ArrayList<>();
+ // this is scary
for (Atom carbon : structure.keySet()) {
if (carbon.getElement() != Element.CARBON || carbonsToIgnore.contains(carbon)) {
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/index/DestroyMolecules.java b/src/main/java/com/petrolpark/destroy/chemistry/index/DestroyMolecules.java
index e3611b65c..a1ada2e86 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/index/DestroyMolecules.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/index/DestroyMolecules.java
@@ -1,5 +1,7 @@
package com.petrolpark.destroy.chemistry.index;
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
import com.petrolpark.destroy.Destroy;
import com.petrolpark.destroy.chemistry.Element;
import com.petrolpark.destroy.chemistry.Formula;
@@ -9,9 +11,9 @@
import com.petrolpark.destroy.chemistry.Molecule.MoleculeBuilder;
import net.minecraft.ChatFormatting;
+import org.openzen.zencode.java.ZenCodeType;
public final class DestroyMolecules {
-
public static final Molecule
ACETAMIDE = builder()
@@ -1100,7 +1102,7 @@ public final class DestroyMolecules {
.charge(1)
.build();
- private static final MoleculeBuilder builder() {
+ public static MoleculeBuilder builder() {
return new MoleculeBuilder(Destroy.MOD_ID);
};
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/index/DestroyReactions.java b/src/main/java/com/petrolpark/destroy/chemistry/index/DestroyReactions.java
index d29c67f43..4ff178f39 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/index/DestroyReactions.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/index/DestroyReactions.java
@@ -11,7 +11,6 @@
import com.petrolpark.destroy.item.DestroyItems;
import com.simibubi.create.AllItems;
import com.simibubi.create.AllTags;
-
import net.minecraft.world.item.Items;
public class DestroyReactions {
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/index/genericreaction/AlcoholDehydration.java b/src/main/java/com/petrolpark/destroy/chemistry/index/genericreaction/AlcoholDehydration.java
index 5ef4e029a..c8b48fb88 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/index/genericreaction/AlcoholDehydration.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/index/genericreaction/AlcoholDehydration.java
@@ -32,7 +32,7 @@ public Reaction generateReaction(GenericReactant reactant) {
for (Atom carbon : structure.moveTo(alcohol.carbon).getBondedAtomsOfElement(Element.CARBON)) {
List hydrogens = structure.moveTo(carbon).getBondedAtomsOfElement(Element.HYDROGEN);
List carbons = structure.getBondedAtomsOfElement(Element.CARBON);
- if (hydrogens.size() + carbons.size() != 4 || hydrogens.size() == 0) continue; // Don't form from non-sp3 alkyl carbons
+ if (hydrogens.size() + carbons.size() != 4 || hydrogens.isEmpty()) continue; // Don't form from non-sp3 alkyl carbons
Formula productStructure = structure.shallowCopy();
productStructure
.remove(hydrogens.get(0))
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/index/genericreaction/AlkeneHydrolysis.java b/src/main/java/com/petrolpark/destroy/chemistry/index/genericreaction/AlkeneHydrolysis.java
index 0d8889a52..6f383d0d9 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/index/genericreaction/AlkeneHydrolysis.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/index/genericreaction/AlkeneHydrolysis.java
@@ -29,6 +29,4 @@ public void transform(ReactionBuilder builder) {
.addCatalyst(DestroyMolecules.SULFURIC_ACID, 1)
.activationEnergy(200f);
};
-
-
};
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/index/group/CarbonylGroup.java b/src/main/java/com/petrolpark/destroy/chemistry/index/group/CarbonylGroup.java
index dcb56ce2b..a63b6c405 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/index/group/CarbonylGroup.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/index/group/CarbonylGroup.java
@@ -15,7 +15,7 @@ public class CarbonylGroup extends Group {
*/
public final boolean isKetone;
- public CarbonylGroup(Atom carbon, Atom oxygen, Boolean isKetone) {
+ public CarbonylGroup(Atom carbon, Atom oxygen, boolean isKetone) {
super();
this.carbon = carbon;
this.oxygen = oxygen;
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/serializer/Edge.java b/src/main/java/com/petrolpark/destroy/chemistry/serializer/Edge.java
index d15b14161..bf0308858 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/serializer/Edge.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/serializer/Edge.java
@@ -6,13 +6,12 @@ public class Edge {
public BondType bondType;
private Node srcNode;
private Node destNode;
- public Boolean marked;
+ public boolean marked = false;
public Edge(Node srcNode, Node destNode, BondType bondType) {
this.srcNode = srcNode;
this.destNode = destNode;
this.bondType = bondType;
- marked = false;
};
public Node getSourceNode() {
diff --git a/src/main/java/com/petrolpark/destroy/chemistry/serializer/Node.java b/src/main/java/com/petrolpark/destroy/chemistry/serializer/Node.java
index 36c131986..e111d7627 100644
--- a/src/main/java/com/petrolpark/destroy/chemistry/serializer/Node.java
+++ b/src/main/java/com/petrolpark/destroy/chemistry/serializer/Node.java
@@ -12,22 +12,19 @@
import com.petrolpark.destroy.chemistry.Bond.BondType;
public class Node {
- private Atom atom;
- public Boolean visited;
- private List edges;
+ private final Atom atom;
+ public boolean visited = false;
private Branch branch;
- private Map sideBranches;
+ private final List edges = new ArrayList<>();
+ private final Map sideBranches = new HashMap<>();
public Node(Atom atom) {
this.atom = atom;
- visited = false;
- edges = new ArrayList<>();
- sideBranches = new HashMap<>();
};
public String serialize() {
String string = getAtom().getElement().getSymbol();
- Boolean isTerminal = true;
+ boolean isTerminal = true;
Edge nextEdge = null;
for (Edge edge : edges) {
if (edge.getSourceNode() == this) {
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/CTDestroy.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/CTDestroy.java
new file mode 100644
index 000000000..89e0c54ac
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/CTDestroy.java
@@ -0,0 +1,130 @@
+package com.petrolpark.destroy.compat.crafttweaker;
+
+import com.blamejared.crafttweaker.api.CraftTweakerAPI;
+import com.blamejared.crafttweaker.api.fluid.CTFluidIngredient;
+import com.blamejared.crafttweaker.api.fluid.IFluidStack;
+import com.blamejared.crafttweaker.api.item.IItemStack;
+import com.blamejared.crafttweaker.api.recipe.component.IRecipeComponent;
+import com.blamejared.crafttweaker.api.tag.CraftTweakerTagRegistry;
+import com.blamejared.crafttweaker.api.tag.type.KnownTag;
+import com.blamejared.crafttweaker.api.util.GenericUtil;
+import com.blamejared.crafttweaker.api.util.IngredientUtil;
+import com.blamejared.crafttweaker.api.util.random.Percentaged;
+import com.google.gson.reflect.TypeToken;
+import com.petrolpark.destroy.mixin.accessor.FluidTagIngredientAccessor;
+import com.simibubi.create.content.processing.recipe.HeatCondition;
+import com.simibubi.create.content.processing.recipe.ProcessingOutput;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipe;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+import com.simibubi.create.foundation.fluid.FluidIngredient;
+import net.minecraft.core.NonNullList;
+import net.minecraft.core.registries.Registries;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.tags.TagKey;
+import net.minecraft.world.Container;
+import net.minecraft.world.level.material.Fluid;
+import net.minecraftforge.fluids.FluidStack;
+import org.apache.logging.log4j.Logger;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class CTDestroy {
+ private static final Logger logger = CraftTweakerAPI.getLogger("Destroy");
+
+ public static Logger getLogger() {
+ return logger;
+ }
+
+
+ // Taken from MIT licensed code https://github.com/jaredlll08/CreateTweaker/blob/1.20.1/forge/src/main/java/com/blamejared/createtweaker/services/ForgePlatformService.java
+ public static FluidIngredient mapFluidIngredients(CTFluidIngredient ingredient) {
+ return ingredient.mapTo(CTDestroy::fromFluidStack, CTDestroy::fromTag, stream -> {
+ throw new IllegalArgumentException("Unable to use a compound ingredient for Create!");
+ });
+ }
+
+ private static FluidIngredient fromFluidStack(IFluidStack stack) {
+ return FluidIngredient.fromFluidStack(stack.getInternal());
+ }
+
+ public static Percentaged mapProcessingResult(ProcessingOutput result) {
+ return IItemStack.of(result.getStack()).percent(result.getChance() * 100);
+ }
+
+ public static ProcessingOutput mapPercentagedToProcessingOutput(Percentaged stack) {
+ return new ProcessingOutput(stack.getData().getInternal(), (float) stack.getPercentage());
+ }
+
+ public static ProcessingOutput mapItemStackToProcessingOutput(IItemStack stack) {
+ return new ProcessingOutput(stack.getInternal(), 1);
+ }
+
+ public static > ProcessingRecipeBuilder withFluidOutputs(ProcessingRecipeBuilder builder, List fluidOutputs) {
+
+ builder.withFluidOutputs(fluidOutputs.stream()
+ .map(IFluidStack::getInternal)
+ .collect(Collectors.toCollection(NonNullList::create)));
+ return builder;
+ }
+
+ public static ProcessingRecipeBuilder> output(ProcessingRecipeBuilder> builder, IFluidStack output) {
+
+ return GenericUtil.uncheck(builder.output(output. getInternal()));
+ }
+
+ public static FluidIngredient fromTag(TagKey tag, int amount) {
+ return FluidIngredient.fromTag(tag, amount);
+ }
+
+ public static CTFluidIngredient mapFluidIngredientsToCT(FluidIngredient ingredient) {
+ if(ingredient instanceof FluidIngredient.FluidTagIngredient fti) {
+ KnownTag tag = CraftTweakerTagRegistry.INSTANCE.knownTagManager(Registries.FLUID)
+ .tag(((FluidTagIngredientAccessor) fti).destroy$getTag());
+ return new CTFluidIngredient.FluidTagWithAmountIngredient(tag.asTagWithAmount());
+ }
+ Optional reduce = ingredient.getMatchingFluidStacks()
+ .stream()
+ .map(IFluidStack::of)
+ .map(IFluidStack::asFluidIngredient)
+ .reduce(CTFluidIngredient::asCompound);
+ return reduce.orElseThrow();
+ }
+
+ public static > boolean doFluidIngredientsConflict(T first, T second) {
+ return IngredientUtil.doIngredientsConflict(first.getFluidIngredients(),
+ second.getFluidIngredients(),
+ FluidIngredient.EMPTY::equals,
+ fluidIngredient -> fluidIngredient.getMatchingFluidStacks().toArray(FluidStack[]::new),
+ FluidStack::containsFluid);
+ }
+
+ public static Stream streamFluidResults(ProcessingRecipe> recipe) {
+ return recipe.getFluidResults()
+ .stream()
+ .map(IFluidStack::of);
+ }
+ public static List getRecipeFluidResults(ProcessingRecipe> recipe) {
+ return streamFluidResults(recipe).toList();
+ }
+
+ public static List getMatchingFluidStacks(FluidIngredient ingredient) {
+ return ingredient.getMatchingFluidStacks()
+ .stream()
+ .map(IFluidStack::of)
+ .collect(Collectors.toList());
+ }
+
+ public static final class RecipeInput {
+ public static final IRecipeComponent HEAT = IRecipeComponent.simple(
+ new ResourceLocation("crafttweaker", "input/heat"),
+ new TypeToken<>() {},
+ Objects::equals
+ );
+
+ private RecipeInput() {}
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/CTMoleculeManager.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/CTMoleculeManager.java
new file mode 100644
index 000000000..07ebf9de0
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/CTMoleculeManager.java
@@ -0,0 +1,89 @@
+package com.petrolpark.destroy.compat.crafttweaker;
+
+import com.blamejared.crafttweaker.api.CraftTweakerAPI;
+import com.blamejared.crafttweaker.api.annotation.BracketResolver;
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.petrolpark.destroy.chemistry.Element;
+import com.petrolpark.destroy.chemistry.Molecule;
+import com.petrolpark.destroy.chemistry.MoleculeTag;
+import com.petrolpark.destroy.compat.crafttweaker.action.RemoveMoleculeAction;
+import org.openzen.zencode.java.ZenCodeType;
+
+/**
+ * Use this class to manage molecules of the mod. Use bracket handler to query
+ * a molecule. Example:
+ *
+ * Use {@link CTMoleculeManager#create(String)} to create a {@link com.petrolpark.destroy.compat.crafttweaker.natives.CTMoleculeBuilder}
+ * if you want to create your own molecule
+ *
+ * When creating a molecule, you may want to specify how does the molecule impact the environment, use
+ * bracket handler. Example: (specifies that the molecule is toxic)
+ * For more details on creation go to {@link com.petrolpark.destroy.compat.crafttweaker.natives.CTMoleculeBuilder}
+ *
+ * Use {@link CTMoleculeManager#removeMolecule(Molecule)} to remove a molecule (all reactions involving this molecule will also be removed)
+ *
+ * If you want to explicitly define a structure of molecule, you may use a structure builder. To access elements in a structure builder
+ * use bracket handler. Example: . Please note, you unfortunately can't create your own elements.
+ *
+ * @docParam this
+ */
+@ZenRegister
+@ZenCodeType.Name("mods.destroy.Molecules")
+@Document("mods/destroy/Molecules")
+public class CTMoleculeManager {
+
+ /**
+ * Creates a molecule builder. Call .build() to build the molecule
+ * @param id ID of the new molecule
+ * @return The {@link com.petrolpark.destroy.compat.crafttweaker.natives.CTMoleculeBuilder}
+ *
+ * @docParam id "tellurium_copper"
+ */
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder create(String id) {
+ return new Molecule.MoleculeBuilder("crafttweaker")
+ .id(id);
+ }
+
+ /**
+ * Gets a molecule by full ID
+ * @param moleculeId Molecule ID to search
+ * @return A {@link com.petrolpark.destroy.compat.crafttweaker.natives.CTMolecule} which was found by ID or null if molecule doesn't exist
+ *
+ * @docParam moleculeId "destroy:water"
+ */
+ @ZenCodeType.Method
+ public static Molecule getMoleculeById(String moleculeId) {
+ return Molecule.getMolecule(moleculeId);
+ }
+
+ /**
+ * Removes this molecule from registry. This makes all reactions involving this molecule to unregister as well.
+ * @param molecule {@link com.petrolpark.destroy.compat.crafttweaker.natives.CTMolecule} to remove
+ *
+ * @docParam molecule
+ */
+ @ZenCodeType.Method
+ public static void removeMolecule(Molecule molecule) {
+ CraftTweakerAPI.apply(new RemoveMoleculeAction(molecule));
+ }
+
+ @ZenCodeType.Method
+ @BracketResolver("molecule")
+ public static Molecule getMolecule(String tokens) {
+ return getMoleculeById(tokens);
+ }
+
+ @ZenCodeType.Method
+ @BracketResolver("moleculetag")
+ public static MoleculeTag getMoleculeTag(String tokens) {
+ return MoleculeTag.MOLECULE_TAGS.get(tokens);
+ }
+
+ @ZenCodeType.Method
+ @BracketResolver("element")
+ public static Element getElement(String tokens) {
+ return Element.valueOf(tokens.toUpperCase());
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/CTReactionManager.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/CTReactionManager.java
new file mode 100644
index 000000000..a94149e3e
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/CTReactionManager.java
@@ -0,0 +1,48 @@
+package com.petrolpark.destroy.compat.crafttweaker;
+
+import com.blamejared.crafttweaker.api.CraftTweakerAPI;
+import com.blamejared.crafttweaker.api.annotation.BracketResolver;
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.petrolpark.destroy.chemistry.Molecule;
+import com.petrolpark.destroy.chemistry.Reaction;
+import com.petrolpark.destroy.compat.crafttweaker.action.RemoveMoleculeAction;
+import com.petrolpark.destroy.compat.crafttweaker.action.RemoveReactionAction;
+import org.openzen.zencode.java.ZenCodeType;
+
+/**
+ * Use this class to manage Reactions of this mod. Use bracket handler to query an existing reaction.
+ * Example:
+ *
+ * Use {@link CTReactionManager#create(String)} to create a {@link com.petrolpark.destroy.compat.crafttweaker.natives.CTReactionBuilder}
+ * if you want to create a new reaction
+ *
+ * For reaction
+ * @docParam this
+ */
+@ZenRegister
+@ZenCodeType.Name("mods.destroy.Reactions")
+@Document("mods/destroy/Reactions")
+public class CTReactionManager {
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder create(String id) {
+ return new Reaction.ReactionBuilder("crafttweaker")
+ .id(id);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction getReactionById(String reactionId) {
+ return Reaction.REACTIONS.get(reactionId);
+ }
+
+ @ZenCodeType.Method
+ public static void removeReaction(Reaction reaction) {
+ CraftTweakerAPI.apply(new RemoveReactionAction(reaction));
+ }
+
+ @ZenCodeType.Method
+ @BracketResolver("reaction")
+ public static Reaction getReaction(String tokens) {
+ return getReactionById(tokens);
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/AddMoleculeAction.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/AddMoleculeAction.java
new file mode 100644
index 000000000..87892a00f
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/AddMoleculeAction.java
@@ -0,0 +1,31 @@
+package com.petrolpark.destroy.compat.crafttweaker.action;
+
+import com.petrolpark.destroy.chemistry.Molecule;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+
+public class AddMoleculeAction extends DestroyAction {
+ private final Molecule molecule;
+
+ public AddMoleculeAction(Molecule molecule) {
+ this.molecule = molecule;
+ }
+ @Override
+ public void undo() {
+ Molecule.removeMolecule(molecule);
+ }
+
+ @Override
+ public String describeUndo() {
+ return "Unregisters the Molecule from the registry and makes it invisible for the reactions";
+ }
+
+ @Override
+ public void apply() {
+ CTDestroy.getLogger().info("Registered " + molecule.getFullID());
+ }
+
+ @Override
+ public String describe() {
+ return "Adds a Molecule to the registry and makes it visible for the reactions";
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/AddReactionAction.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/AddReactionAction.java
new file mode 100644
index 000000000..f33ae1347
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/AddReactionAction.java
@@ -0,0 +1,33 @@
+package com.petrolpark.destroy.compat.crafttweaker.action;
+
+import com.petrolpark.destroy.chemistry.Reaction;
+import com.petrolpark.destroy.compat.jei.category.ReactionCategory;
+import com.petrolpark.destroy.recipe.ReactionRecipe;
+
+public class AddReactionAction extends DestroyAction {
+ private final Reaction reaction;
+
+ public AddReactionAction(Reaction reaction) {
+ this.reaction = reaction;
+ }
+ @Override
+ public void undo() {
+ Reaction.unregisterReaction(reaction);
+ ReactionCategory.RECIPES.remove(reaction);
+ }
+
+ @Override
+ public String describe() {
+ return "Adds a reaction to the registry and makes it available to do in a vat";
+ }
+
+ @Override
+ public String describeUndo() {
+ return "Unregisters the reaction and makes it impossible to make in a vat";
+ }
+
+ @Override
+ public void apply() {
+ if (reaction.includeInJei()) ReactionCategory.RECIPES.put(reaction, ReactionRecipe.create("crafttweaker", reaction));
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/DestroyAction.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/DestroyAction.java
new file mode 100644
index 000000000..4e95c7b80
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/DestroyAction.java
@@ -0,0 +1,16 @@
+package com.petrolpark.destroy.compat.crafttweaker.action;
+
+import com.blamejared.crafttweaker.api.action.base.IUndoableAction;
+
+public abstract class DestroyAction implements IUndoableAction {
+
+ @Override
+ public String describe() {
+ return "An internal Destroy action";
+ }
+
+ @Override
+ public String systemName() {
+ return "Destroy";
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/RemoveMoleculeAction.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/RemoveMoleculeAction.java
new file mode 100644
index 000000000..8d9e1c8ec
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/RemoveMoleculeAction.java
@@ -0,0 +1,31 @@
+package com.petrolpark.destroy.compat.crafttweaker.action;
+
+import com.petrolpark.destroy.chemistry.Molecule;
+
+public class RemoveMoleculeAction extends DestroyAction {
+
+ private final Molecule molecule;
+
+ public RemoveMoleculeAction(Molecule molecule) {
+ this.molecule = molecule;
+ }
+ @Override
+ public void undo() {
+ Molecule.addMolecule(molecule);
+ }
+
+ @Override
+ public String describe() {
+ return "Removes the molecule from the registry and makes it unavailable for the vat reactions";
+ }
+
+ @Override
+ public String describeUndo() {
+ return "Adds the molecule back and makes it available for the reactions";
+ }
+
+ @Override
+ public void apply() {
+ Molecule.removeMolecule(molecule);
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/RemoveReactionAction.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/RemoveReactionAction.java
new file mode 100644
index 000000000..61ce6bf09
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/action/RemoveReactionAction.java
@@ -0,0 +1,39 @@
+package com.petrolpark.destroy.compat.crafttweaker.action;
+
+import com.petrolpark.destroy.chemistry.Reaction;
+import com.petrolpark.destroy.compat.jei.category.ReactionCategory;
+import com.petrolpark.destroy.recipe.ReactionRecipe;
+
+public class RemoveReactionAction extends DestroyAction {
+ private final Reaction reaction;
+ private final ReactionRecipe reactionRecipe;
+
+ public RemoveReactionAction(Reaction reaction) {
+ this.reaction = reaction;
+ this.reactionRecipe = ReactionCategory.RECIPES.get(reaction);
+ }
+
+ @Override
+ public void undo() {
+ Reaction.registerReaction(reaction);
+ if(reactionRecipe != null) {
+ ReactionCategory.RECIPES.put(reaction, reactionRecipe);
+ }
+ }
+
+ @Override
+ public String describe() {
+ return "Removes the reaction from the registry";
+ }
+
+ @Override
+ public String describeUndo() {
+ return "Adds the reaction back into the registry";
+ }
+
+ @Override
+ public void apply() {
+ Reaction.unregisterReaction(reaction);
+ ReactionCategory.RECIPES.remove(reaction);
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTAtom.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTAtom.java
new file mode 100644
index 000000000..45088019c
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTAtom.java
@@ -0,0 +1,30 @@
+package com.petrolpark.destroy.compat.crafttweaker.natives;
+
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.blamejared.crafttweaker_annotations.annotations.NativeTypeRegistration;
+import com.jozufozu.flywheel.core.PartialModel;
+import com.petrolpark.destroy.chemistry.Atom;
+import com.petrolpark.destroy.chemistry.Element;
+import org.openzen.zencode.java.ZenCodeType;
+
+@ZenRegister
+@Document("mods/destroy/Atom")
+@NativeTypeRegistration(value = Atom.class, zenCodeName = "mods.destroy.Atom")
+public class CTAtom {
+
+ @ZenCodeType.Method
+ public static Element getElement(Atom internal) {
+ return internal.getElement();
+ }
+
+ @ZenCodeType.Method
+ public static PartialModel getPartial(Atom internal) {
+ return internal.getPartial();
+ }
+
+ @ZenCodeType.Method
+ public static boolean isHydrogen(Atom internal) {
+ return internal.isHydrogen();
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTElement.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTElement.java
new file mode 100644
index 000000000..99a38b5be
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTElement.java
@@ -0,0 +1,59 @@
+package com.petrolpark.destroy.compat.crafttweaker.natives;
+
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.blamejared.crafttweaker_annotations.annotations.NativeTypeRegistration;
+import com.jozufozu.flywheel.core.PartialModel;
+import com.petrolpark.destroy.chemistry.Element;
+import com.petrolpark.destroy.client.gui.MoleculeRenderer;
+import org.openzen.zencode.java.ZenCodeType;
+
+@ZenRegister
+@Document("mods/destroy/Element")
+@NativeTypeRegistration(value = Element.class, zenCodeName = "mods.destroy.Element")
+public class CTElement {
+ @ZenCodeType.Method
+ public static String getSymbol(Element internal) {
+ return internal.getSymbol();
+ }
+
+ @ZenCodeType.Method
+ public static Float getMass(Element internal) {
+ return internal.getMass();
+ }
+
+ @ZenCodeType.Method
+ public static Float getElectronegativity(Element internal) {
+ return internal.getElectronegativity();
+ }
+
+ @ZenCodeType.Method
+ public static boolean isValidValency(Element internal, double valency) {
+ return internal.isValidValency(valency);
+ }
+
+ @ZenCodeType.Method
+ public static double getNextLowestValency(Element internal, double valency) {
+ return internal.getNextLowestValency(valency);
+ }
+
+ @ZenCodeType.Method
+ public static double getMaxValency(Element internal) {
+ return internal.getMaxValency();
+ }
+
+ @ZenCodeType.Method
+ public static MoleculeRenderer.Geometry getGeometry(Element internal, int connections) {
+ return internal.getGeometry(connections);
+ }
+
+ @ZenCodeType.Method
+ public static PartialModel getPartial(Element internal) {
+ return internal.getPartial();
+ }
+
+ @ZenCodeType.Method
+ public static void setPartial(Element internal, PartialModel partial) {
+ internal.setPartial(partial);
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTFormula.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTFormula.java
new file mode 100644
index 000000000..be476c56f
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTFormula.java
@@ -0,0 +1,152 @@
+package com.petrolpark.destroy.compat.crafttweaker.natives;
+
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.blamejared.crafttweaker_annotations.annotations.NativeConstructor;
+import com.blamejared.crafttweaker_annotations.annotations.NativeTypeRegistration;
+import com.petrolpark.destroy.chemistry.Atom;
+import com.petrolpark.destroy.chemistry.Bond;
+import com.petrolpark.destroy.chemistry.Element;
+import com.petrolpark.destroy.chemistry.Formula;
+import org.openzen.zencode.java.ZenCodeType;
+
+import java.util.List;
+import java.util.Set;
+
+@ZenRegister
+@Document("mods/destroy/Formula")
+@NativeTypeRegistration(
+ value = Formula.class,
+ zenCodeName = "mods.destroy.Formula",
+ constructors = {
+ @NativeConstructor({
+ @NativeConstructor.ConstructorParameter(type = Atom.class, name = "startingAtom")
+ })
+ }
+)
+public class CTFormula {
+ @ZenCodeType.Method
+ public static Formula moveTo(Formula internal, Atom atom) {
+ return internal.moveTo(atom);
+ }
+
+ @ZenCodeType.Method
+ public static Formula setStartingAtom(Formula internal, Atom atom) {
+ return internal.setStartingAtom(atom);
+ }
+
+ @ZenCodeType.StaticExpansionMethod
+ public static Formula atom(Element element) {
+ return Formula.atom(element);
+ }
+
+ @ZenCodeType.StaticExpansionMethod
+ public static Formula carbonChain(int length) {
+ return Formula.carbonChain(length);
+ }
+
+ @ZenCodeType.StaticExpansionMethod
+ public static Formula alcohol() {
+ return Formula.alcohol();
+ }
+
+ @ZenCodeType.Method
+ public static Formula addAtom(Formula internal, Element element) {
+ return internal.addAtom(element);
+ }
+
+ @ZenCodeType.Method
+ public static Formula addAtom(Formula internal, Element element, Bond.BondType bondType) {
+ return internal.addAtom(element, bondType);
+ }
+
+ @ZenCodeType.Method
+ public static Formula addAtom(Formula internal, Atom atom) {
+ return internal.addAtom(atom);
+ }
+
+ @ZenCodeType.Method
+ public static Formula addAtom(Formula internal, Atom atom, Bond.BondType bondType) {
+ return internal.addAtom(atom, bondType);
+ }
+
+ @ZenCodeType.Method
+ public static Formula addGroup(Formula internal, Formula group) {
+ return internal.addGroup(group);
+ }
+
+ @ZenCodeType.Method
+ public static Formula addGroup(Formula internal, Formula group, boolean isSideGroup) {
+ return internal.addGroup(group, isSideGroup);
+ }
+
+ @ZenCodeType.Method
+ public static Formula joinFormulae(Formula formula1, Formula formula2, Bond.BondType bondType) {
+ return Formula.joinFormulae(formula1, formula2, bondType);
+ }
+
+ @ZenCodeType.Method
+ public static Formula addGroup(Formula internal, Formula group, boolean isSideGroup, Bond.BondType bondType) {
+ return internal.addGroup(group, isSideGroup, bondType);
+ }
+
+ @ZenCodeType.Method
+ public static boolean isCyclic(Formula internal) {
+ return internal.isCyclic();
+ }
+
+ @ZenCodeType.Method
+ public static Formula remove(Formula internal, Atom atom) {
+ return internal.remove(atom);
+ }
+
+ @ZenCodeType.Method
+ public static Formula replace(Formula internal, Atom oldAtom, Atom newAtom) {
+ return internal.replace(oldAtom, newAtom);
+ }
+
+ @ZenCodeType.Method
+ public static Formula replaceBondTo(Formula internal, Atom otherAtom, Bond.BondType bondType) {
+ return internal.replaceBondTo(otherAtom, bondType);
+ }
+
+ @ZenCodeType.Method
+ public static Formula addCarbonyl(Formula internal) {
+ return internal.addCarbonyl();
+ }
+
+ @ZenCodeType.Method
+ public static Formula addAllHydrogens(Formula internal) {
+ return internal.addAllHydrogens();
+ }
+
+ @ZenCodeType.Method
+ public static Set getAllAtoms(Formula internal) {
+ return internal.getAllAtoms();
+ }
+
+ @ZenCodeType.Method
+ public static List getBondedAtomsOfElement(Formula internal, Element element) {
+ return internal.getBondedAtomsOfElement(element);
+ }
+
+ @ZenCodeType.Method
+ public static double getTotalBonds(Formula internal, List bonds) {
+ return internal.getTotalBonds(bonds);
+ }
+
+ @ZenCodeType.Method
+ public static String toString(Formula internal) {
+ return internal.serialize();
+ }
+
+ @ZenCodeType.StaticExpansionMethod
+ public static Formula deserialize(String FROWNSstring) {
+ return Formula.deserialize(FROWNSstring);
+ }
+
+ @ZenCodeType.Method
+ public static float getCarbocationStability(Formula internal, Atom carbon, boolean isCarbanion) {
+ return internal.getCarbocationStability(carbon, isCarbanion);
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTMixture.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTMixture.java
new file mode 100644
index 000000000..e0adda944
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTMixture.java
@@ -0,0 +1,216 @@
+package com.petrolpark.destroy.compat.crafttweaker.natives;
+
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker.api.fluid.CTFluidIngredient;
+import com.blamejared.crafttweaker.api.fluid.IFluidStack;
+import com.blamejared.crafttweaker.api.util.random.Percentaged;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.blamejared.crafttweaker_annotations.annotations.NativeTypeRegistration;
+import com.petrolpark.destroy.chemistry.Mixture;
+import com.petrolpark.destroy.chemistry.Molecule;
+import com.petrolpark.destroy.chemistry.MoleculeTag;
+import com.petrolpark.destroy.chemistry.ReadOnlyMixture;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.petrolpark.destroy.fluid.DestroyFluids;
+import com.petrolpark.destroy.fluid.ingredient.*;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.network.chat.Component;
+import net.minecraftforge.fluids.FluidStack;
+import org.jetbrains.annotations.Nullable;
+import org.openzen.zencode.java.ZenCodeType;
+
+import java.text.DecimalFormat;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+
+@ZenRegister
+@Document("mods/destroy/Mixture")
+@NativeTypeRegistration(value = Mixture.class, zenCodeName = "mods.destroy.Mixture")
+public class CTMixture {
+ /**
+ *
+ * @param data data of molecules and concentration
+ * @param temperature In kelvins, 0 for unspecified
+ * @return
+ */
+ @ZenCodeType.StaticExpansionMethod
+ public static Mixture create(List> data, @ZenCodeType.OptionalFloat(0) float temperature, @ZenCodeType.OptionalString String translationKey) {
+ Mixture mixture = new Mixture();
+ for(Percentaged moleculePercentage : data) {
+ mixture.addMolecule(moleculePercentage.getData(), (float) moleculePercentage.getPercentage());
+ }
+ mixture.setTemperature(ReadOnlyMixture.BASE_TEMPERATURE);
+ if(!"".equals(translationKey)) {
+ mixture.setTranslationKey(translationKey);
+ }
+ return mixture;
+ }
+
+ @ZenCodeType.StaticExpansionMethod
+ public static IFluidStack createMixtureStack(Mixture mixture, @ZenCodeType.OptionalInt(1000) int amount) {
+ FluidStack fluidStack = new FluidStack(DestroyFluids.MIXTURE.get(), amount);
+ fluidStack.getOrCreateTag().put("Mixture", mixture.writeNBT());
+ return IFluidStack.of(fluidStack);
+ }
+
+ @ZenCodeType.StaticExpansionMethod
+ public static CTFluidIngredient createMoleculeFluidIngredient(Percentaged moleculeData, @ZenCodeType.OptionalInt(1000) int amount) {
+ return createMoleculeFluidIngredient(moleculeData.getData(), (float) moleculeData.getPercentage(), amount);
+ }
+
+ @ZenCodeType.StaticExpansionMethod
+ public static CTFluidIngredient createMoleculeFluidIngredient(Molecule molecule, float concentration, @ZenCodeType.OptionalInt(1000) int amount) {
+ MoleculeFluidIngredient ingredient = new MoleculeFluidIngredient();
+ ingredient.molecule = molecule;
+ ingredient.setConcentrations(concentration);
+ CompoundTag tag = getTag(ingredient);
+ IFluidStack stack = IFluidStack.of(DestroyFluids.MIXTURE.get(), amount, tag);
+ return new CTFluidIngredient.FluidStackIngredient(stack);
+ }
+
+ @ZenCodeType.StaticExpansionMethod
+ public static CTFluidIngredient createSaltFluidIngredient(Molecule cation, Molecule anion, float concentration, @ZenCodeType.OptionalInt(1000) int amount) {
+ SaltFluidIngredient ingredient = new SaltFluidIngredient();
+ ingredient.cation = cation;
+ ingredient.anion = anion;
+ ingredient.setConcentrations(concentration);
+ IFluidStack stack = IFluidStack.of(DestroyFluids.MIXTURE.get(), amount, getTag(ingredient));
+ return new CTFluidIngredient.FluidStackIngredient(stack);
+ }
+
+ @ZenCodeType.StaticExpansionMethod
+ public static CTFluidIngredient createMoleculeTagFluidIngredient(MoleculeTag tag, float concentration, @ZenCodeType.OptionalInt(1000) int amount) {
+ MoleculeTagFluidIngredient ingredient = new MoleculeTagFluidIngredient();
+ ingredient.tag = tag;
+ ingredient.setConcentrations(concentration);
+ IFluidStack stack = IFluidStack.of(DestroyFluids.MIXTURE.get(), amount, getTag(ingredient));
+ return new CTFluidIngredient.FluidStackIngredient(stack);
+ }
+
+ @ZenCodeType.StaticExpansionMethod
+ public static CTFluidIngredient createIonFluidIngredient(float concentration, @ZenCodeType.OptionalInt(1000) int amount) {
+ IonFluidIngredient ingredient = new IonFluidIngredient();
+ ingredient.setConcentrations(concentration);
+ IFluidStack stack = IFluidStack.of(DestroyFluids.MIXTURE.get(), amount, getTag(ingredient));
+ return new CTFluidIngredient.FluidStackIngredient(stack);
+ }
+
+ private static CompoundTag getTag(MixtureFluidIngredient> ingredient) {
+ CompoundTag tag = new CompoundTag();
+ ingredient.addNBT(tag);
+ tag.putString("MixtureFluidIngredientSubtype", ingredient.getType().getMixtureFluidIngredientSubtype());
+ return tag;
+ }
+
+ @ZenCodeType.StaticExpansionMethod
+ public static Mixture pure(Molecule molecule) {
+ return Mixture.pure(molecule);
+ }
+
+ @ZenCodeType.StaticExpansionMethod
+ public static Mixture mix(Map mixtures) {
+ return Mixture.mix(mixtures);
+ }
+
+ @ZenCodeType.Method
+ @ZenCodeType.Setter("temperature")
+ public static Mixture setTemperature(Mixture internal, float temperature) {
+ return internal.setTemperature(temperature);
+ }
+
+ @ZenCodeType.Method
+ public static Mixture addMolecule(Mixture internal, Molecule molecule, float concentration) {
+ return internal.addMolecule(molecule, concentration);
+ }
+
+ @ZenCodeType.Method
+ public static List getContents(Mixture internal, boolean excludeNovel) {
+ return internal.getContents(excludeNovel);
+ }
+
+ @ZenCodeType.Method
+ public static float getConcentrationOf(Mixture internal, Molecule molecule) {
+ return internal.getConcentrationOf(molecule);
+ }
+
+ @ZenCodeType.Method
+ public static boolean isAtEquilibrium(Mixture internal) {
+ return internal.isAtEquilibrium();
+ }
+
+ @ZenCodeType.Method
+ public static void disturbEquilibrium(Mixture internal) {
+ internal.disturbEquilibrium();
+ }
+
+ @ZenCodeType.Method
+ public static void heat(Mixture internal, float energyDensity) {
+ internal.heat(energyDensity);
+ }
+
+ @ZenCodeType.Method
+ public static void scale(Mixture internal, float volumeIncreaseFactor) {
+ internal.scale(volumeIncreaseFactor);
+ }
+
+ @ZenCodeType.Method
+ public static float getVolumetricHeatCapacity(Mixture internal) {
+ return internal.getVolumetricHeatCapacity();
+ }
+
+ @ZenCodeType.Method
+ public static int getColor(Mixture internal) {
+ return internal.getColor();
+ }
+
+ @ZenCodeType.Method
+ public static void setTranslationKey(Mixture internal, String translationKey) {
+ internal.setTranslationKey(translationKey);
+ }
+
+ @ZenCodeType.Method
+ public static float getTemperature(Mixture internal) {
+ return internal.getTemperature();
+ }
+
+ @ZenCodeType.Method
+ public static boolean isEmpty(Mixture internal) {
+ return internal.isEmpty();
+ }
+
+ @ZenCodeType.Method
+ public static float getTotalConcentration(Mixture internal) {
+ return internal.getTotalConcentration();
+ }
+
+ @ZenCodeType.Method
+ public static boolean hasUsableMolecule(Mixture internal, Molecule molecule, float minConcentration, float maxConcentration, @Nullable Predicate ignore) {
+ return internal.hasUsableMolecule(molecule, minConcentration, maxConcentration, ignore);
+ }
+
+ @ZenCodeType.Method
+ public static boolean hasUsableMolecules(Mixture internal, Predicate molecules, float minConcentration, float maxConcentration, @Nullable Predicate ignore) {
+ return internal.hasUsableMolecules(molecules, minConcentration, maxConcentration, ignore);
+ }
+
+ @ZenCodeType.Method
+ public static String getContentsString(Mixture internal) {
+ return internal.getContentsString();
+ }
+
+ @ZenCodeType.Method
+ public static List getContentsTooltip(Mixture internal, boolean iupac, boolean monospace, boolean useMoles, int amount, DecimalFormat concentrationFormatter) {
+ return internal.getContentsTooltip(iupac, monospace, useMoles, amount, concentrationFormatter);
+ }
+
+ @ZenCodeType.Operator(ZenCodeType.OperatorType.MUL)
+ public static IFluidStack mulMod(Mixture internal, int amount) {
+ return createMixtureStack(internal, amount);
+ }
+
+ @ZenCodeType.Caster(implicit = true)
+ public static IFluidStack castToFluid(Mixture expanded) {
+ return mulMod(expanded, 1000);
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTMolecule.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTMolecule.java
new file mode 100644
index 000000000..58b7df11f
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTMolecule.java
@@ -0,0 +1,186 @@
+package com.petrolpark.destroy.compat.crafttweaker.natives;
+
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker.api.util.random.Percentaged;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.blamejared.crafttweaker_annotations.annotations.NativeTypeRegistration;
+import com.petrolpark.destroy.chemistry.Atom;
+import com.petrolpark.destroy.chemistry.Molecule;
+import com.petrolpark.destroy.chemistry.MoleculeTag;
+import com.petrolpark.destroy.chemistry.Reaction;
+import net.minecraft.network.chat.Component;
+import org.openzen.zencode.java.ZenCodeType;
+
+import java.util.List;
+import java.util.Set;
+
+@ZenRegister
+@Document("mods/destroy/Molecule")
+@NativeTypeRegistration(value = Molecule.class, zenCodeName = "mods.destroy.Molecule")
+public class CTMolecule {
+
+ @ZenCodeType.Method
+ public static Molecule getEquivalent(Molecule internal) {
+ return internal.getEquivalent();
+ }
+
+ @ZenCodeType.Method
+ public static String getFROWNSCode(Molecule internal) {
+ return internal.getFROWNSCode();
+ }
+
+ @ZenCodeType.Method
+ public static int getCharge(Molecule internal) {
+ return internal.getCharge();
+ }
+
+ @ZenCodeType.Method
+ public static float getMass(Molecule internal) {
+ return internal.getMass();
+ }
+
+ @ZenCodeType.Method
+ public static float getDensity(Molecule internal) {
+ return internal.getDensity();
+ }
+
+ @ZenCodeType.Method
+ public static float getPureConcentration(Molecule internal) {
+ return internal.getPureConcentration();
+ }
+
+ @ZenCodeType.Method
+ public static float getBoilingPoint(Molecule internal) {
+ return internal.getBoilingPoint();
+ }
+
+ @ZenCodeType.Method
+ public static float getDipoleMoment(Molecule internal) {
+ return internal.getDipoleMoment();
+ }
+
+ @ZenCodeType.Method
+ public static float getMolarHeatCapacity(Molecule internal) {
+ return internal.getMolarHeatCapacity();
+ }
+
+ @ZenCodeType.Method
+ public static float getLatentHeat(Molecule internal) {
+ return internal.getLatentHeat();
+ }
+
+ @ZenCodeType.Method
+ public static boolean isCyclic(Molecule internal) {
+ return internal.isCyclic();
+ }
+
+ @ZenCodeType.Method
+ public static Set getAtoms(Molecule internal) {
+ return internal.getAtoms();
+ }
+
+ @ZenCodeType.Method
+ public static boolean isHypothetical(Molecule internal) {
+ return internal.isHypothetical();
+ }
+
+ @ZenCodeType.Method
+ public static Set getTags(Molecule internal) {
+ return internal.getTags();
+ }
+
+ @ZenCodeType.Method
+ public static boolean hasTag(Molecule internal, MoleculeTag tag) {
+ return internal.hasTag(tag);
+ }
+
+ @ZenCodeType.Method
+ public static boolean isNovel(Molecule internal) {
+ return internal.isNovel();
+ }
+
+ @ZenCodeType.Method
+ public static String getStructuralFormula(Molecule internal) {
+ return internal.getStructuralFormula();
+ }
+
+ @ZenCodeType.Method
+
+ public static float getCarbocationStability(Molecule internal, Atom carbon, boolean isCarbanion) {
+ return internal.getCarbocationStability(carbon, isCarbanion);
+ }
+
+ @ZenCodeType.Method
+ public static String getTranslationKey(Molecule internal, boolean iupac) {
+ return internal.getTranslationKey(iupac);
+ }
+
+ @ZenCodeType.Method
+ public static int getColor(Molecule internal) {
+ return internal.getColor();
+ }
+
+ @ZenCodeType.Method
+ public static boolean isColorless(Molecule internal) {
+ return internal.isColorless();
+ }
+
+ @ZenCodeType.Method
+ public static Component getName(Molecule internal, boolean iupac) {
+ return internal.getName(iupac);
+ }
+
+ @ZenCodeType.Method
+ public static String getId(Molecule internal) {
+ return internal.getFullID();
+ }
+
+ @ZenCodeType.Method
+ public static void addReactantReaction(Molecule internal, Reaction reaction) {
+ internal.addReactantReaction(reaction);
+ }
+
+ /**
+ * Mark this Molecule as being a necessary reactant in the given {@link CTReaction}.
+ * There should never be any need to call this method (it is done automatically when {@link CTReactionBuilder#build building} a Reaction).
+ * @param reaction Reaction
+ */
+
+ @ZenCodeType.Method
+ public static void addProductReaction(Molecule internal, Reaction reaction) {
+ internal.addProductReaction(reaction);
+ }
+
+ /**
+ * Get the list of {@link CTReaction} of which this Molecule is a necessary Reactant.
+ * @return List of Reactions ordered by declaration
+ */
+ @ZenCodeType.Method
+ public static List getReactantReactions(Molecule internal) {
+ return internal.getReactantReactions();
+ }
+
+ /**
+ * Get the list of {@link CTReaction} by which this Molecule is made.
+ * @return List of Reactions ordered by declaration
+ */
+ @ZenCodeType.Method
+ public static List getProductReactions(Molecule internal) {
+ return internal.getProductReactions();
+ }
+
+ @ZenCodeType.Getter("commandString")
+ public static String getCommandString(Molecule expanded) {
+ return "".formatted(expanded.getFullID());
+ }
+
+ @ZenCodeType.Operator(ZenCodeType.OperatorType.MUL)
+ public static Percentaged opMul(Molecule expanded, double amount) {
+ return new Percentaged<>(expanded, amount, CTMolecule::getCommandString);
+ }
+
+ @ZenCodeType.Caster(implicit = true)
+ public static Percentaged castPercentaged(Molecule expanded) {
+ return opMul(expanded, 100.0D);
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTMoleculeBuilder.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTMoleculeBuilder.java
new file mode 100644
index 000000000..6a92ed010
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTMoleculeBuilder.java
@@ -0,0 +1,98 @@
+package com.petrolpark.destroy.compat.crafttweaker.natives;
+
+import com.blamejared.crafttweaker.api.CraftTweakerAPI;
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.blamejared.crafttweaker_annotations.annotations.NativeTypeRegistration;
+import com.petrolpark.destroy.chemistry.Formula;
+import com.petrolpark.destroy.chemistry.Molecule;
+import com.petrolpark.destroy.chemistry.MoleculeTag;
+import com.petrolpark.destroy.compat.crafttweaker.action.AddMoleculeAction;
+import org.openzen.zencode.java.ZenCodeType;
+
+@ZenRegister
+@Document("mods/destroy/MoleculeBuilder")
+@NativeTypeRegistration(value = Molecule.MoleculeBuilder.class, zenCodeName = "mods.destroy.MoleculeBuilder")
+public class CTMoleculeBuilder {
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder id(Molecule.MoleculeBuilder internal, String id) {
+ return internal.id(id);
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder formula(Molecule.MoleculeBuilder internal, String formula) {
+ return internal.structure(Formula.deserialize(formula));
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder structure(Molecule.MoleculeBuilder internal, Formula formula) {
+ return internal.structure(formula);
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder density(Molecule.MoleculeBuilder internal, float density) {
+ return internal.density(density);
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder charge(Molecule.MoleculeBuilder internal, int charge) {
+ return internal.charge(charge);
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder boilingPoint(Molecule.MoleculeBuilder internal, float boilingPoint) {
+ return internal.boilingPoint(boilingPoint);
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder boilingPointInKelvins(Molecule.MoleculeBuilder internal, float boilingPoint) {
+ return internal.boilingPointInKelvins(boilingPoint);
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder dipoleMoment(Molecule.MoleculeBuilder internal, int dipoleMoment) {
+ return internal.dipoleMoment(dipoleMoment);
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder specificHeatCapacity(Molecule.MoleculeBuilder internal, float specificHeatCapacity) {
+ return internal.specificHeatCapacity(specificHeatCapacity);
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder molarHeatCapacity(Molecule.MoleculeBuilder internal, float molarHeatCapacity) {
+ return internal.molarHeatCapacity(molarHeatCapacity);
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder latentHeat(Molecule.MoleculeBuilder internal, float latentHeat) {
+ return internal.latentHeat(latentHeat);
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder translationKey(Molecule.MoleculeBuilder internal, String translationKey) {
+ return internal.translationKey(translationKey);
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder color(Molecule.MoleculeBuilder internal, int color) {
+ return internal.color(color);
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder hypothetical(Molecule.MoleculeBuilder internal) {
+ return internal.hypothetical();
+ }
+
+ @ZenCodeType.Method
+ public static Molecule.MoleculeBuilder tag(Molecule.MoleculeBuilder internal, MoleculeTag... tags) {
+ return internal.tag(tags);
+ }
+
+ @ZenCodeType.Method
+ public static Molecule build(Molecule.MoleculeBuilder internal) {
+ Molecule result = internal.build();
+ CraftTweakerAPI.apply(new AddMoleculeAction(result));
+ return result;
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTReaction.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTReaction.java
new file mode 100644
index 000000000..7dabde259
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTReaction.java
@@ -0,0 +1,201 @@
+package com.petrolpark.destroy.compat.crafttweaker.natives;
+
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.blamejared.crafttweaker_annotations.annotations.NativeTypeRegistration;
+import com.petrolpark.destroy.chemistry.IItemReactant;
+import com.petrolpark.destroy.chemistry.Molecule;
+import com.petrolpark.destroy.chemistry.Reaction;
+import com.petrolpark.destroy.chemistry.ReactionResult;
+import org.openzen.zencode.java.ZenCodeType;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+@ZenRegister
+@Document("mods/destroy/Reaction")
+@NativeTypeRegistration(value = Reaction.class, zenCodeName = "mods.destroy.Reaction")
+public class CTReaction {
+
+ /**
+ * Whether this Molecule gets consumed in this Reaction (does not include catalysts).
+ *
+ * @param molecule
+ */
+ @ZenCodeType.Method
+ public static boolean containsReactant(Reaction internal, Molecule molecule) {
+ return internal.containsReactant(molecule);
+ }
+
+ /**
+ * Whether this Molecule is created in this Reaction.
+ *
+ * @param molecule
+ */
+ @ZenCodeType.Method
+ public static boolean containsProduct(Reaction internal, Molecule molecule) {
+ return internal.containsProduct(molecule);
+ }
+
+ /**
+ * All Molecules which are consumed in this Reaction (but not their molar ratios).
+ */
+ @ZenCodeType.Method
+ public static Set getReactants(Reaction internal) {
+ return internal.getReactants();
+ }
+
+ /**
+ * Whether this Reaction needs any Item Stack as a {@link IItemReactant}. Even if this is
+ * {@code true}, the Reaction may still have {@link IItemReactant#isCatalyst Item Stack catalysts}.
+ */
+ @ZenCodeType.Method
+ public static boolean consumesItem(Reaction internal) {
+ return internal.consumesItem();
+ }
+
+ /**
+ * Get the {@link IItemReactant} for this Reaction.
+ */
+ @ZenCodeType.Method
+ public static List getItemReactants(Reaction internal) {
+ return internal.getItemReactants();
+ }
+
+ @ZenCodeType.Method
+ public static float getMolesPerItem(Reaction internal) {
+ return internal.getMolesPerItem();
+ }
+
+ /**
+ * Whether this Reaction needs UV light to proceed.
+ */
+ @ZenCodeType.Method
+ public static boolean needsUV(Reaction internal) {
+ return internal.needsUV();
+ }
+
+ /**
+ * All Molecules which are created in this Reaction (but not their molar ratios).
+ */
+ @ZenCodeType.Method
+ public static Set getProducts(Reaction internal) {
+ return internal.getProducts();
+ }
+ @ZenCodeType.Method
+ public static float getActivationEnergy(Reaction internal) {
+ return internal.getActivationEnergy();
+ }
+ @ZenCodeType.Method
+ public static float getPreexponentialFactor(Reaction internal) {
+ return internal.getPreexponentialFactor();
+ }
+
+ /**
+ * The rate constant of this Reaction at the given temperature.
+ *
+ * @param temperature (in kelvins).
+ */
+ @ZenCodeType.Method
+ public static float getRateConstant(Reaction internal, float temperature) {
+ return internal.getRateConstant(temperature);
+ }
+
+ @ZenCodeType.Method
+ public static float getEnthalpyChange(Reaction internal) {
+ return internal.getEnthalpyChange();
+ }
+
+ /**
+ * Whether this Reaction has a {@link com.petrolpark.destroy.compat.crafttweaker.natives.reactionresult.CTReactionResult}.
+ */
+ @ZenCodeType.Method
+ public static boolean hasResult(Reaction internal) {
+ return internal.hasResult();
+ }
+
+ /**
+ * The {@link com.petrolpark.destroy.compat.crafttweaker.natives.reactionresult.CTReactionResult} of this Reaction, which occurs once a set
+ * number of moles of Reaction have occured.
+ *
+ * @return {@code null} if this Reaction has no result.
+ */
+ @ZenCodeType.Method
+ public static ReactionResult getResult(Reaction internal) {
+ return internal.getResult();
+ }
+
+ /**
+ * Get the fully unique ID for this Reaction, in the format {@code :
+ * }, for example {@code destroy:chloroform_fluorination}.
+ */
+ @ZenCodeType.Method
+ public static String getFullID(Reaction internal) {
+ return internal.getFullID();
+ }
+
+ /**
+ * Whether this Reaction should be displayed in the list of Reactions in JEI.
+ */
+ @ZenCodeType.Method
+ public static boolean includeInJei(Reaction internal) {
+ return internal.includeInJei();
+ }
+
+ /**
+ * Whether this Reaction should be displayed in JEI with an equilibrium arrow rather than a normal one.
+ */
+ @ZenCodeType.Method
+ public static boolean displayAsReversible(Reaction internal) {
+ return internal.displayAsReversible();
+ }
+
+ /**
+ * If this is the 'forward' half of a reversible Reaction, this contains the reverse Reaction. This is so JEI
+ * knows the products of the forward Reaction are the reactants of the reverse, and vice versa. If this is not
+ * part of a reversible Reaction, this is empty. This is just for display; if a Reaction has a reverse and is needed
+ * for logic (e.g. Reacting in a Mixture) it should not be accessed in this way.
+ */
+ @ZenCodeType.Method
+ public static Optional getReverseReactionForDisplay(Reaction internal) {
+ return internal.getReverseReactionForDisplay();
+ }
+
+ /**
+ * Get the stoichometric ratio of this {@link CTMolecule} or catalyst in this Reaction.
+ *
+ * @param reactant
+ * @return {@code 0} if this Molecule is not a reactant
+ */
+ @ZenCodeType.Method
+ public static int getReactantMolarRatio(Reaction internal, Molecule reactant) {
+ return internal.getReactantMolarRatio(reactant);
+ }
+
+ /**
+ * Get the stoichometric ratio of this {@link CTMolecule} in this Reaction.
+ *
+ * @param product
+ * @return {@code 0} if this Molecule is not a product
+ */
+ @ZenCodeType.Method
+ public static int getProductMolarRatio(Reaction internal, Molecule product) {
+ return internal.getProductMolarRatio(product);
+ }
+
+ /**
+ * Get every {@link CTMolecule} reactant and catalyst in this Reaction, mapped to their
+ * orders in the rate equation.
+ */
+ @ZenCodeType.Method
+ public static Map getOrders(Reaction internal) {
+ return internal.getOrders();
+ }
+
+ @ZenCodeType.Method
+ public static String getID(Reaction internal) {
+ return internal.getFullID();
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTReactionBuilder.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTReactionBuilder.java
new file mode 100644
index 000000000..301565c25
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/CTReactionBuilder.java
@@ -0,0 +1,144 @@
+package com.petrolpark.destroy.compat.crafttweaker.natives;
+
+import com.blamejared.crafttweaker.api.CraftTweakerAPI;
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker.api.ingredient.IIngredient;
+import com.blamejared.crafttweaker.api.item.IItemStack;
+import com.blamejared.crafttweaker.api.tag.manager.ITagManager;
+import com.blamejared.crafttweaker.api.tag.type.KnownTag;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.blamejared.crafttweaker_annotations.annotations.NativeTypeRegistration;
+import com.petrolpark.destroy.chemistry.IItemReactant;
+import com.petrolpark.destroy.chemistry.Molecule;
+import com.petrolpark.destroy.chemistry.Reaction;
+import com.petrolpark.destroy.chemistry.ReactionResult;
+import com.petrolpark.destroy.compat.crafttweaker.action.AddReactionAction;
+import net.minecraft.world.item.Item;
+import org.openzen.zencode.java.ZenCodeType;
+import org.openzen.zencode.shared.Tag;
+
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Supplier;
+
+@ZenRegister
+@Document("mods/destroy/ReactionBuilder")
+@NativeTypeRegistration(value = Reaction.ReactionBuilder.class, zenCodeName = "mods.destroy.ReactionBuilder")
+public class CTReactionBuilder {
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder addReactant(Reaction.ReactionBuilder internal, Molecule molecule) {
+ return internal.addReactant(molecule);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder addReactant(Reaction.ReactionBuilder internal, Molecule molecule, int ratio) {
+ return internal.addReactant(molecule, ratio);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder addReactant(Reaction.ReactionBuilder internal, Molecule molecule, int ratio, int order) {
+ return internal.addReactant(molecule, ratio, order);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder setOrder(Reaction.ReactionBuilder internal, Molecule molecule, int order) {
+ return internal.setOrder(molecule, order);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder addItemReactant(Reaction.ReactionBuilder internal, IItemStack itemReactant, float moles) {
+ return internal.addItemReactant(new IItemReactant.SimpleItemReactant(() -> itemReactant.getInternal().getItem()), moles);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder addSimpleItemReactant(Reaction.ReactionBuilder internal, Supplier- item, float moles) {
+ return internal.addSimpleItemReactant(item, moles);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder addSimpleItemTagReactant(Reaction.ReactionBuilder internal, KnownTag
- tag, float moles) {
+ return internal.addSimpleItemTagReactant(tag.getTagKey(), moles);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder addSimpleItemCatalyst(Reaction.ReactionBuilder internal, Supplier
- item, float moles) {
+ return internal.addSimpleItemCatalyst(item, moles);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder addSimpleItemTagCatalyst(Reaction.ReactionBuilder internal, KnownTag
- tag, float moles) {
+ return internal.addSimpleItemTagCatalyst(tag.getTagKey(), moles);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder requireUV(Reaction.ReactionBuilder internal) {
+ return internal.requireUV();
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder addProduct(Reaction.ReactionBuilder internal, Molecule molecule) {
+ return internal.addProduct(molecule);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder addProduct(Reaction.ReactionBuilder internal, Molecule molecule, int ratio) {
+ return internal.addProduct(molecule, ratio);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder addCatalyst(Reaction.ReactionBuilder internal, Molecule molecule, int order) {
+ return internal.addCatalyst(molecule, order);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder dontIncludeInJei(Reaction.ReactionBuilder internal) {
+ return internal.dontIncludeInJei();
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder displayAsReversible(Reaction.ReactionBuilder internal) {
+ return internal.displayAsReversible();
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder id(Reaction.ReactionBuilder internal, String id) {
+ return internal.id(id);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder preexponentialFactor(Reaction.ReactionBuilder internal, float preexponentialFactor) {
+ return internal.preexponentialFactor(preexponentialFactor);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder activationEnergy(Reaction.ReactionBuilder internal, float activationEnergy) {
+ return internal.activationEnergy(activationEnergy);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder enthalpyChange(Reaction.ReactionBuilder internal, float enthalpyChange) {
+ return internal.enthalpyChange(enthalpyChange);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder withResult(Reaction.ReactionBuilder internal, float moles, BiFunction reactionresultFactory) {
+ return internal.withResult(moles, reactionresultFactory);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction acid(Reaction.ReactionBuilder internal, Molecule acid, Molecule conjugateBase, float pKa) {
+ return internal.acid(acid, conjugateBase, pKa);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction.ReactionBuilder reverseReaction(Reaction.ReactionBuilder internal, Consumer reverseReactionModifier) {
+ return internal.reverseReaction(reverseReactionModifier);
+ }
+
+ @ZenCodeType.Method
+ public static Reaction build(Reaction.ReactionBuilder internal) {
+ Reaction result = internal.build();
+ CraftTweakerAPI.apply(new AddReactionAction(result));
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/reactionresult/CTReactionResult.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/reactionresult/CTReactionResult.java
new file mode 100644
index 000000000..a18b44889
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/natives/reactionresult/CTReactionResult.java
@@ -0,0 +1,4 @@
+package com.petrolpark.destroy.compat.crafttweaker.natives.reactionresult;
+
+public class CTReactionResult {
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/AgingRecipeHandler.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/AgingRecipeHandler.java
new file mode 100644
index 000000000..4a38bdbbd
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/AgingRecipeHandler.java
@@ -0,0 +1,37 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.handler;
+
+import com.blamejared.crafttweaker.api.fluid.IFluidStack;
+import com.blamejared.crafttweaker.api.ingredient.IIngredient;
+import com.blamejared.crafttweaker.api.recipe.handler.IRecipeHandler;
+import com.blamejared.crafttweaker.api.recipe.manager.base.IRecipeManager;
+import com.blamejared.crafttweaker.api.util.random.Percentaged;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.petrolpark.destroy.recipe.AgingRecipe;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+
+import java.util.stream.Collectors;
+
+@IRecipeHandler.For(AgingRecipe.class)
+public class AgingRecipeHandler implements IDestroyRecipeHandler {
+
+ @Override
+ public String dumpToCommandString(IRecipeManager super AgingRecipe> manager, AgingRecipe recipe) {
+ return String.format(
+ ".addRecipe(\"%s\", %s, [%s], %s, %s);",
+ recipe.getId(),
+ IFluidStack.of(recipe.getRequiredFluid()).getCommandString(),
+ recipe.getIngredients()
+ .stream()
+ .map(IIngredient::fromIngredient)
+ .map(IIngredient::getCommandString)
+ .collect(Collectors.joining(", ")),
+ IFluidStack.of(recipe.getResult()).getCommandString(),
+ recipe.getProcessingDuration()
+ );
+ }
+
+ @Override
+ public ProcessingRecipeBuilder.ProcessingRecipeFactory factory() {
+ return AgingRecipe::new;
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/CentrifugationRecipeHandler.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/CentrifugationRecipeHandler.java
new file mode 100644
index 000000000..8e50614a4
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/CentrifugationRecipeHandler.java
@@ -0,0 +1,68 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.handler;
+
+import com.blamejared.crafttweaker.api.fluid.CTFluidIngredient;
+import com.blamejared.crafttweaker.api.fluid.IFluidStack;
+import com.blamejared.crafttweaker.api.recipe.component.BuiltinRecipeComponents;
+import com.blamejared.crafttweaker.api.recipe.component.IDecomposedRecipe;
+import com.blamejared.crafttweaker.api.recipe.handler.IRecipeHandler;
+import com.blamejared.crafttweaker.api.recipe.manager.base.IRecipeManager;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.petrolpark.destroy.recipe.CentrifugationRecipe;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+import net.minecraft.core.NonNullList;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraftforge.fluids.FluidStack;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.stream.Collectors;
+
+@IRecipeHandler.For(CentrifugationRecipe.class)
+public class CentrifugationRecipeHandler implements IDestroyRecipeHandler {
+
+ @Override
+ public String dumpToCommandString(IRecipeManager super CentrifugationRecipe> manager, CentrifugationRecipe recipe) {
+ return String.format(
+ ".addRecipe(\"%s\", %s, [%s], %s);",
+ recipe.getId(),
+ CTDestroy.getMatchingFluidStacks(recipe.getRequiredFluid()).stream()
+ .map(IFluidStack::getCommandString)
+ .collect(Collectors.joining(", ")),
+ recipe.getFluidResults()
+ .stream()
+ .map(fluid -> IFluidStack.of(fluid).getCommandString())
+ .collect(Collectors.joining(", ")),
+ recipe.getProcessingDuration()
+ );
+ }
+
+ @Override
+ public Optional decompose(IRecipeManager super CentrifugationRecipe> manager, CentrifugationRecipe recipe) {
+ List results = new ArrayList<>(2);
+ for(FluidStack fluidResult : recipe.getFluidResults()) {
+ results.add(IFluidStack.of(fluidResult));
+ }
+ IDecomposedRecipe result = IDecomposedRecipe.builder()
+ .with(BuiltinRecipeComponents.Input.FLUID_INGREDIENTS, List.of(IFluidStack.of(recipe.getRequiredFluid()).asFluidIngredient()))
+ .with(BuiltinRecipeComponents.Output.FLUIDS, results)
+ .with(BuiltinRecipeComponents.Processing.TIME, recipe.getProcessingDuration())
+ .build();
+ return Optional.of(result);
+ }
+
+ @Override
+ public Optional recompose(IRecipeManager super CentrifugationRecipe> manager, ResourceLocation name, IDecomposedRecipe recipe) {
+ ProcessingRecipeBuilder builder = new ProcessingRecipeBuilder<>(factory(), name);
+ CTFluidIngredient source = recipe.getOrThrow(BuiltinRecipeComponents.Input.FLUID_INGREDIENTS).get(0);
+ builder.withFluidIngredients(CTDestroy.mapFluidIngredients(source));
+ CTDestroy.withFluidOutputs(builder, recipe.getOrThrow(BuiltinRecipeComponents.Output.FLUIDS));
+ builder.duration(recipe.getOrThrow(BuiltinRecipeComponents.Processing.TIME).get(0));
+ return Optional.of(builder.build());
+ }
+ @Override
+ public ProcessingRecipeBuilder.ProcessingRecipeFactory factory() {
+ return CentrifugationRecipe::new;
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/ChargingRecipeHandler.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/ChargingRecipeHandler.java
new file mode 100644
index 000000000..80976a00e
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/ChargingRecipeHandler.java
@@ -0,0 +1,29 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.handler;
+
+import com.blamejared.crafttweaker.api.ingredient.IIngredient;
+import com.blamejared.crafttweaker.api.recipe.component.BuiltinRecipeComponents;
+import com.blamejared.crafttweaker.api.recipe.component.IDecomposedRecipe;
+import com.blamejared.crafttweaker.api.recipe.handler.IRecipeHandler;
+import com.blamejared.crafttweaker.api.recipe.manager.base.IRecipeManager;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.petrolpark.destroy.recipe.ChargingRecipe;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+import net.minecraft.core.NonNullList;
+import net.minecraft.resources.ResourceLocation;
+
+import java.util.List;
+import java.util.Optional;
+
+@IRecipeHandler.For(ChargingRecipe.class)
+public class ChargingRecipeHandler implements IDestroyRecipeHandler {
+
+ @Override
+ public String getRecipeTypeName() {
+ return "charging";
+ }
+
+ @Override
+ public ProcessingRecipeBuilder.ProcessingRecipeFactory factory() {
+ return ChargingRecipe::new;
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/DistillationRecipeHandler.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/DistillationRecipeHandler.java
new file mode 100644
index 000000000..60587f059
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/DistillationRecipeHandler.java
@@ -0,0 +1,66 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.handler;
+
+import com.blamejared.crafttweaker.api.fluid.CTFluidIngredient;
+import com.blamejared.crafttweaker.api.fluid.IFluidStack;
+import com.blamejared.crafttweaker.api.ingredient.IIngredient;
+import com.blamejared.crafttweaker.api.recipe.component.BuiltinRecipeComponents;
+import com.blamejared.crafttweaker.api.recipe.component.IDecomposedRecipe;
+import com.blamejared.crafttweaker.api.recipe.handler.IRecipeHandler;
+import com.blamejared.crafttweaker.api.recipe.manager.base.IRecipeManager;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.petrolpark.destroy.recipe.DistillationRecipe;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+import net.minecraft.core.NonNullList;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraftforge.fluids.FluidStack;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@IRecipeHandler.For(DistillationRecipe.class)
+public class DistillationRecipeHandler implements IDestroyRecipeHandler {
+ @Override
+ public String dumpToCommandString(IRecipeManager super DistillationRecipe> manager, DistillationRecipe recipe) {
+ return String.format(
+ ".addRecipe(\"%s\", , %s, [%s]);",
+ recipe.getId(),
+ recipe.getRequiredHeat().name().toLowerCase(Locale.ENGLISH),
+ CTDestroy.getMatchingFluidStacks(recipe.getRequiredFluid()).stream()
+ .map(IFluidStack::getCommandString)
+ .collect(Collectors.joining(", ")),
+ recipe.getFluidResults()
+ .stream()
+ .map(fluid -> IFluidStack.of(fluid).getCommandString())
+ .collect(Collectors.joining(", "))
+ );
+ }
+
+ @Override
+ public Optional decompose(IRecipeManager super DistillationRecipe> manager, DistillationRecipe recipe) {
+ List results = new ArrayList<>(recipe.getFractions());
+ for(FluidStack fluidResult : recipe.getFluidResults()) {
+ results.add(IFluidStack.of(fluidResult));
+ }
+ IDecomposedRecipe result = IDecomposedRecipe.builder()
+ .with(BuiltinRecipeComponents.Input.FLUID_INGREDIENTS, List.of(IFluidStack.of(recipe.getRequiredFluid()).asFluidIngredient()))
+ .with(BuiltinRecipeComponents.Output.FLUIDS, results)
+ .build();
+ return Optional.of(result);
+ }
+
+ @Override
+ public Optional recompose(IRecipeManager super DistillationRecipe> manager, ResourceLocation name, IDecomposedRecipe recipe) {
+ ProcessingRecipeBuilder builder = new ProcessingRecipeBuilder<>(factory(), name);
+ CTFluidIngredient source = recipe
+ .getOrThrow(BuiltinRecipeComponents.Input.FLUID_INGREDIENTS)
+ .get(0);
+ builder.withFluidIngredients(CTDestroy.mapFluidIngredients(source));
+ CTDestroy.withFluidOutputs(builder, recipe.getOrThrow(BuiltinRecipeComponents.Output.FLUIDS));
+ return Optional.of(builder.build());
+ }
+
+ @Override
+ public ProcessingRecipeBuilder.ProcessingRecipeFactory factory() {
+ return DistillationRecipe::new;
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/ElectrolysisRecipeHandler.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/ElectrolysisRecipeHandler.java
new file mode 100644
index 000000000..3e5301965
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/ElectrolysisRecipeHandler.java
@@ -0,0 +1,13 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.handler;
+
+import com.blamejared.crafttweaker.api.recipe.handler.IRecipeHandler;
+import com.petrolpark.destroy.recipe.ElectrolysisRecipe;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+
+@IRecipeHandler.For(ElectrolysisRecipe.class)
+public class ElectrolysisRecipeHandler implements IDestroyRecipeHandler {
+ @Override
+ public ProcessingRecipeBuilder.ProcessingRecipeFactory factory() {
+ return ElectrolysisRecipe::new;
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/ExtrusionRecipeHandler.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/ExtrusionRecipeHandler.java
new file mode 100644
index 000000000..d4d6acdc8
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/ExtrusionRecipeHandler.java
@@ -0,0 +1,20 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.handler;
+
+import com.blamejared.crafttweaker.api.recipe.handler.IRecipeHandler;
+import com.blamejared.crafttweaker.api.recipe.manager.base.IRecipeManager;
+import com.petrolpark.destroy.compat.crafttweaker.recipes.manager.IDestroyRecipeManager;
+import com.petrolpark.destroy.recipe.ExtrusionRecipe;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+
+@IRecipeHandler.For(ExtrusionRecipe.class)
+public class ExtrusionRecipeHandler implements ITwoItemsRecipeHandler {
+ @Override
+ public String getRecipeTypeName() {
+ return "extrusion";
+ }
+
+ @Override
+ public ProcessingRecipeBuilder.ProcessingRecipeFactory factory() {
+ return ExtrusionRecipe::new;
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/IDestroyRecipeHandler.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/IDestroyRecipeHandler.java
new file mode 100644
index 000000000..d961290fc
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/IDestroyRecipeHandler.java
@@ -0,0 +1,131 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.handler;
+
+import com.blamejared.crafttweaker.api.fluid.IFluidStack;
+import com.blamejared.crafttweaker.api.ingredient.IIngredient;
+import com.blamejared.crafttweaker.api.item.IItemStack;
+import com.blamejared.crafttweaker.api.recipe.component.BuiltinRecipeComponents;
+import com.blamejared.crafttweaker.api.recipe.component.IDecomposedRecipe;
+import com.blamejared.crafttweaker.api.recipe.handler.IRecipeHandler;
+import com.blamejared.crafttweaker.api.recipe.manager.base.IRecipeManager;
+import com.blamejared.crafttweaker.api.util.IngredientUtil;
+import com.blamejared.crafttweaker.api.util.random.Percentaged;
+import com.mojang.datafixers.util.Either;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipe;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+import net.minecraft.core.NonNullList;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.item.crafting.Recipe;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Locale;
+import java.util.Optional;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public interface IDestroyRecipeHandler> extends IRecipeHandler {
+
+ default String getRecipeTypeName() {
+ return null;
+ }
+
+ @Override
+ default String dumpToCommandString(IRecipeManager super T> iRecipeManager, T recipe) {
+ Either>, IFluidStack> result;
+ List fluidResults = CTDestroy.getRecipeFluidResults(recipe);
+ if(!fluidResults.isEmpty()) {
+ result = Either.right(fluidResults.get(0));
+ } else {
+ result = Either.left(recipe.getRollableResults()
+ .stream()
+ .map(CTDestroy::mapProcessingResult));
+ }
+
+ return String.format(".addRecipe(\"%s\", , [%s], [%s], [%s], [%s], %s);",
+ recipe.getId(),
+ recipe.getRequiredHeat().name().toLowerCase(Locale.ENGLISH),
+ result.map(results -> results.map(Percentaged::getCommandString)
+ .collect(Collectors.joining(", ")), IFluidStack::getCommandString),
+ recipe.getIngredients()
+ .stream()
+ .map(IIngredient::fromIngredient)
+ .map(IIngredient::getCommandString)
+ .collect(Collectors.joining(", ")),
+ recipe.getFluidIngredients()
+ .stream()
+ .map(CTDestroy::getMatchingFluidStacks)
+ .flatMap(Collection::stream)
+ .map(IFluidStack::getCommandString)
+ .collect(Collectors.joining(", ")),
+ CTDestroy.streamFluidResults(recipe)
+ .map(IFluidStack::getCommandString)
+ .collect(Collectors.joining(", ")),
+ recipe.getProcessingDuration()
+ );
+ }
+ @Override
+ default > boolean doesConflict(IRecipeManager super T> manager, T firstRecipe, U secondRecipe) {
+ if(isGoodRecipe(firstRecipe, secondRecipe)) {
+ final T second = (T) secondRecipe;
+ if(firstRecipe.getIngredients().size() != second.getIngredients().size() || !firstRecipe.getRequiredHeat()
+ .equals(second.getRequiredHeat())) {
+ return false;
+ }
+ return IngredientUtil.doIngredientsConflict(firstRecipe.getIngredients(), secondRecipe.getIngredients())
+ && CTDestroy.doFluidIngredientsConflict(firstRecipe, second);
+
+ }
+
+ return false;
+ }
+
+ default boolean isGoodRecipe(Recipe> self, Recipe> other) {
+ return other.getClass().isAssignableFrom(self.getClass());
+ }
+
+ default Optional decompose(IRecipeManager super T> manager, T recipe) {
+ return Optional.of(IDecomposedRecipe.builder()
+ .with(BuiltinRecipeComponents.Input.INGREDIENTS, recipe.getIngredients()
+ .stream()
+ .map(IIngredient::fromIngredient)
+ .toList())
+ .with(BuiltinRecipeComponents.Output.CHANCED_ITEMS, recipe.getRollableResults()
+ .stream()
+ .map(CTDestroy::mapProcessingResult)
+ .toList())
+ .with(BuiltinRecipeComponents.Input.FLUID_INGREDIENTS, recipe.getFluidIngredients()
+ .stream()
+ .map(CTDestroy::mapFluidIngredientsToCT)
+ .toList())
+ .with(BuiltinRecipeComponents.Output.FLUIDS, CTDestroy.getRecipeFluidResults(recipe)
+ .stream()
+ .toList())
+ .with(BuiltinRecipeComponents.Processing.TIME, recipe.getProcessingDuration())
+ .with(CTDestroy.RecipeInput.HEAT, recipe.getRequiredHeat())
+ .build()
+ );
+ }
+
+ default Optional recompose(IRecipeManager super T> manager, ResourceLocation name, IDecomposedRecipe recipe) {
+ ProcessingRecipeBuilder builder = new ProcessingRecipeBuilder<>(factory(), name);
+ builder.withItemIngredients(recipe.getOrThrow(BuiltinRecipeComponents.Input.INGREDIENTS)
+ .stream()
+ .map(IIngredient::asVanillaIngredient)
+ .collect(Collectors.toCollection(NonNullList::create)));
+ builder.withItemOutputs(recipe.getOrThrow(BuiltinRecipeComponents.Output.CHANCED_ITEMS)
+ .stream()
+ .map(CTDestroy::mapPercentagedToProcessingOutput)
+ .collect(Collectors.toCollection(NonNullList::create)));
+ builder.withFluidIngredients(recipe.getOrThrow(BuiltinRecipeComponents.Input.FLUID_INGREDIENTS)
+ .stream()
+ .map(CTDestroy::mapFluidIngredients)
+ .collect(Collectors.toCollection(NonNullList::create)));
+ CTDestroy.withFluidOutputs(builder, recipe.getOrThrow(BuiltinRecipeComponents.Output.FLUIDS));
+ builder.duration(recipe.getOrThrowSingle(BuiltinRecipeComponents.Processing.TIME));
+ builder.requiresHeat(recipe.getOrThrowSingle(CTDestroy.RecipeInput.HEAT));
+ return Optional.of(builder.build());
+ }
+
+ ProcessingRecipeBuilder.ProcessingRecipeFactory factory();
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/ITwoItemsRecipeHandler.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/ITwoItemsRecipeHandler.java
new file mode 100644
index 000000000..c5dc81b5d
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/handler/ITwoItemsRecipeHandler.java
@@ -0,0 +1,44 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.handler;
+
+import com.blamejared.crafttweaker.api.ingredient.IIngredient;
+import com.blamejared.crafttweaker.api.recipe.component.BuiltinRecipeComponents;
+import com.blamejared.crafttweaker.api.recipe.component.IDecomposedRecipe;
+import com.blamejared.crafttweaker.api.recipe.manager.base.IRecipeManager;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.petrolpark.destroy.recipe.ChargingRecipe;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipe;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.item.crafting.Recipe;
+
+import java.util.List;
+import java.util.Optional;
+
+public interface ITwoItemsRecipeHandler> extends IDestroyRecipeHandler {
+ @Override
+ default String dumpToCommandString(IRecipeManager super T> iRecipeManager, T recipe) {
+ return String.format(
+ ".addRecipe(\"%s\", %s, %s);",
+ recipe.getId(),
+ IIngredient.fromIngredient(recipe.getIngredients().get(0)).getCommandString(),
+ CTDestroy.mapProcessingResult(recipe.getRollableResults().get(0)).getCommandString()
+ );
+ }
+
+ @Override
+ default Optional decompose(IRecipeManager super T> manager, T recipe) {
+ IDecomposedRecipe result = IDecomposedRecipe.builder()
+ .with(BuiltinRecipeComponents.Input.INGREDIENTS, List.of(IIngredient.fromIngredient(recipe.getIngredients().get(0))))
+ .with(BuiltinRecipeComponents.Output.CHANCED_ITEMS, List.of(CTDestroy.mapProcessingResult(recipe.getRollableResults().get(0))))
+ .build();
+ return Optional.of(result);
+ }
+
+ @Override
+ default Optional recompose(IRecipeManager super T> manager, ResourceLocation name, IDecomposedRecipe recipe) {
+ ProcessingRecipeBuilder builder = new ProcessingRecipeBuilder<>(factory(), name);
+ builder.withItemIngredients(recipe.getOrThrow(BuiltinRecipeComponents.Input.INGREDIENTS).get(0).asVanillaIngredient());
+ builder.withItemOutputs(CTDestroy.mapPercentagedToProcessingOutput(recipe.getOrThrow(BuiltinRecipeComponents.Output.CHANCED_ITEMS).get(0)));
+ return Optional.of(builder.build());
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/AgingRecipeHandler.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/AgingRecipeHandler.java
new file mode 100644
index 000000000..b468b60a5
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/AgingRecipeHandler.java
@@ -0,0 +1,54 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.manager;
+
+import com.blamejared.crafttweaker.api.CraftTweakerAPI;
+import com.blamejared.crafttweaker.api.action.recipe.ActionAddRecipe;
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker.api.fluid.CTFluidIngredient;
+import com.blamejared.crafttweaker.api.fluid.IFluidStack;
+import com.blamejared.crafttweaker.api.ingredient.IIngredient;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.petrolpark.destroy.recipe.AgingRecipe;
+import com.petrolpark.destroy.recipe.DestroyRecipeTypes;
+import com.petrolpark.destroy.recipe.DistillationRecipe;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+import net.minecraft.resources.ResourceLocation;
+import org.openzen.zencode.java.ZenCodeType;
+
+@ZenRegister
+@Document("mods/destroy/AgingManager")
+@ZenCodeType.Name("mods.destroy.AgingManager")
+public class AgingRecipeHandler implements IDestroyRecipeManager {
+
+ /**
+ * Adds an aging recipe to the aging barrel
+ * @param name Name of the recipe
+ * @param input Input fluid of the recipe
+ * @param items Additional items to the aging process
+ * @param result Resulting fluid
+ * @param processingTime Processing time of the recipe in ticks
+ *
+ * @docParam name "wine_aging"
+ * @docParam input
+ * @docParam items [, ]
+ * @docParam result per 1 bucket of input
+ * @docParam processingTime 1200
+ */
+ @ZenCodeType.Method
+ public void addRecipe(String name, CTFluidIngredient input, IIngredient[] items, IFluidStack result, @ZenCodeType.OptionalInt(1200) int processingTime) {
+ name = fixRecipeName(name);
+ ResourceLocation resourceLocation = new ResourceLocation("crafttweaker", name);
+ ProcessingRecipeBuilder builder = new ProcessingRecipeBuilder<>(getSerializer().getFactory(), resourceLocation);
+ builder.withFluidIngredients(CTDestroy.mapFluidIngredients(input));
+ for(IIngredient itemIngredient : items) {
+ builder.require(itemIngredient.asVanillaIngredient());
+ }
+ CTDestroy.output(builder, result);
+ CraftTweakerAPI.apply(new ActionAddRecipe<>(this, builder.build()));
+ }
+
+ @Override
+ public DestroyRecipeTypes getDestroyRecipeType() {
+ return DestroyRecipeTypes.AGING;
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/CentrifugationRecipeManager.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/CentrifugationRecipeManager.java
new file mode 100644
index 000000000..e7ecb0685
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/CentrifugationRecipeManager.java
@@ -0,0 +1,54 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.manager;
+
+import com.blamejared.crafttweaker.api.CraftTweakerAPI;
+import com.blamejared.crafttweaker.api.action.recipe.ActionAddRecipe;
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker.api.fluid.CTFluidIngredient;
+import com.blamejared.crafttweaker.api.fluid.IFluidStack;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.petrolpark.destroy.recipe.CentrifugationRecipe;
+import com.petrolpark.destroy.recipe.DestroyRecipeTypes;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+import net.minecraft.resources.ResourceLocation;
+import org.openzen.zencode.java.ZenCodeType;
+
+@ZenRegister
+@Document("mods/destroy/CentrifugationManager")
+@ZenCodeType.Name("mods.destroy.CentrifugationManager")
+public class CentrifugationRecipeManager implements IDestroyRecipeManager {
+
+ /**
+ * Adds recipe to the centrifuge
+ * @param name Name of the recipe
+ * @param input The input fluid
+ * @param output 2 output fluids per 1 mB of input
+ * @param processingTime Processing time of this recipe in ticks
+ *
+ * @docParam name "split_lava"
+ * @docParam input
+ * @docParam output [, * 200]
+ */
+ @ZenCodeType.Method
+ public void addRecipe(String name, CTFluidIngredient input, IFluidStack[] output, @ZenCodeType.OptionalInt(1200) int processingTime) {
+ name = fixRecipeName(name);
+
+ checkMixtureOrThrow(input);
+ if(output.length != 2) {
+ throw new IllegalArgumentException("Centrifugation recipe should have exactly 2 outputs");
+ }
+ ResourceLocation resourceLocation = new ResourceLocation("crafttweaker", name);
+ ProcessingRecipeBuilder builder = new ProcessingRecipeBuilder<>(getSerializer().getFactory(), resourceLocation);
+ builder.withFluidIngredients(CTDestroy.mapFluidIngredients(input));
+ for(IFluidStack fraction : output) {
+ CTDestroy.output(builder, fraction);
+ }
+ builder.duration(processingTime);
+ CraftTweakerAPI.apply(new ActionAddRecipe<>(this, builder.build()));
+ }
+
+ @Override
+ public DestroyRecipeTypes getDestroyRecipeType() {
+ return DestroyRecipeTypes.CENTRIFUGATION;
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/ChargingRecipeManager.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/ChargingRecipeManager.java
new file mode 100644
index 000000000..d67acc827
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/ChargingRecipeManager.java
@@ -0,0 +1,49 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.manager;
+
+import com.blamejared.crafttweaker.api.CraftTweakerAPI;
+import com.blamejared.crafttweaker.api.action.recipe.ActionAddRecipe;
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker.api.fluid.CTFluidIngredient;
+import com.blamejared.crafttweaker.api.fluid.IFluidStack;
+import com.blamejared.crafttweaker.api.ingredient.IIngredient;
+import com.blamejared.crafttweaker.api.item.IItemStack;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.petrolpark.destroy.recipe.CentrifugationRecipe;
+import com.petrolpark.destroy.recipe.ChargingRecipe;
+import com.petrolpark.destroy.recipe.DestroyRecipeTypes;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.item.crafting.Ingredient;
+import org.openzen.zencode.java.ZenCodeType;
+
+@ZenRegister
+@Document("mods/destroy/ChargingManager")
+@ZenCodeType.Name("mods.destroy.ChargingManager")
+public class ChargingRecipeManager implements IDestroyRecipeManager {
+
+ /**
+ * Adds a recipe to the dynamo machine. (This feature is likely to get removed or heavily modified in future release of Destroy)
+ * @param name Name of the recipe
+ * @param input Item to charge
+ * @param output Output item
+ *
+ * @docParam name "charge_iron"
+ * @docParam input
+ * @docParam output
+ */
+ @ZenCodeType.Method
+ public void addRecipe(String name, IIngredient input, IItemStack output) {
+ name = fixRecipeName(name);
+ ResourceLocation resourceLocation = new ResourceLocation("crafttweaker", name);
+ ProcessingRecipeBuilder builder = new ProcessingRecipeBuilder<>(getSerializer().getFactory(), resourceLocation);
+ builder.withItemIngredients(input.asVanillaIngredient());
+ builder.withItemOutputs(CTDestroy.mapItemStackToProcessingOutput(output));
+ CraftTweakerAPI.apply(new ActionAddRecipe<>(this, builder.build()));
+ }
+
+ @Override
+ public DestroyRecipeTypes getDestroyRecipeType() {
+ return DestroyRecipeTypes.CHARGING;
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/DistillationRecipeManager.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/DistillationRecipeManager.java
new file mode 100644
index 000000000..880c7fc57
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/DistillationRecipeManager.java
@@ -0,0 +1,53 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.manager;
+
+import com.blamejared.crafttweaker.api.CraftTweakerAPI;
+import com.blamejared.crafttweaker.api.action.recipe.ActionAddRecipe;
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker.api.fluid.CTFluidIngredient;
+import com.blamejared.crafttweaker.api.fluid.IFluidStack;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.petrolpark.destroy.recipe.DestroyRecipeTypes;
+import com.petrolpark.destroy.recipe.DistillationRecipe;
+import com.simibubi.create.content.processing.recipe.HeatCondition;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+import net.minecraft.resources.ResourceLocation;
+import org.openzen.zencode.java.ZenCodeType;
+
+@ZenRegister
+@Document("mods/destroy/DistillationManager")
+@ZenCodeType.Name("mods.destroy.DistillationManager")
+public class DistillationRecipeManager implements IDestroyRecipeManager {
+
+ /**
+ * Adds recipe to the distillation tower.
+ * @param name Name of the recipe
+ * @param heat Heat required
+ * @param input Input fluid, can't be a mixture
+ * @param fractions How much of other fluids should be created per 1 mB of input
+ *
+ * @docParam name "distill_fluid"
+ * @docParam heat
+ * @docParam input
+ */
+ @ZenCodeType.Method
+ public void addRecipe(String name, HeatCondition heat, CTFluidIngredient input, IFluidStack[] fractions) {
+ name = fixRecipeName(name);
+
+ checkMixtureOrThrow(input);
+
+ ResourceLocation resourceLocation = new ResourceLocation("crafttweaker", name);
+ ProcessingRecipeBuilder builder = new ProcessingRecipeBuilder<>(getSerializer().getFactory(), resourceLocation);
+ builder.withFluidIngredients(CTDestroy.mapFluidIngredients(input));
+ for(IFluidStack fraction : fractions) {
+ CTDestroy.output(builder, fraction);
+ }
+ builder.requiresHeat(heat);
+ CraftTweakerAPI.apply(new ActionAddRecipe<>(this, builder.build()));
+ }
+
+ @Override
+ public DestroyRecipeTypes getDestroyRecipeType() {
+ return DestroyRecipeTypes.DISTILLATION;
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/ElectrolysisRecipeManager.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/ElectrolysisRecipeManager.java
new file mode 100644
index 000000000..de10e8a9a
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/ElectrolysisRecipeManager.java
@@ -0,0 +1,128 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.manager;
+
+import com.blamejared.crafttweaker.api.CraftTweakerAPI;
+import com.blamejared.crafttweaker.api.action.recipe.ActionAddRecipe;
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker.api.fluid.CTFluidIngredient;
+import com.blamejared.crafttweaker.api.fluid.IFluidStack;
+import com.blamejared.crafttweaker.api.ingredient.IIngredientWithAmount;
+import com.blamejared.crafttweaker.api.item.IItemStack;
+import com.blamejared.crafttweaker.api.util.random.Percentaged;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.petrolpark.destroy.recipe.DestroyRecipeTypes;
+import com.petrolpark.destroy.recipe.ElectrolysisRecipe;
+import com.simibubi.create.content.processing.recipe.HeatCondition;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+import com.simibubi.create.foundation.fluid.FluidIngredient;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.item.crafting.Ingredient;
+import org.openzen.zencode.java.ZenCodeType;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+@ZenRegister
+@Document("mods/destroy/ElectrolysisManager")
+@ZenCodeType.Name("mods.destroy.ElectrolysisManager")
+public class ElectrolysisRecipeManager implements IDestroyRecipeManager {
+
+ /**
+ * Adds an Electrolysis recipe that outputs ItemStacks.
+ *
+ * @param name The name of the recipe.
+ * @param heat The required heat of the recipe.
+ * @param outputs The output ItemStacks of the recipe.
+ * @param itemInputs The item inputs of the recipe.
+ * @param fluidInputs The optional fluid inputs of the recipe.
+ * @param duration The duration of the recipe in ticks.
+ *
+ * @docParam name "electrolysed"
+ * @docParam heat
+ * @docParam outputs [ % 50, , ( * 2) % 12]
+ * @docParam itemInputs [ * 2]
+ * @docParam fluidInputs [ * 250]
+ * @docParam duration 200
+ */
+ @ZenCodeType.Method
+ public void addRecipe(String name, HeatCondition heat, Percentaged[] outputs, IIngredientWithAmount[] itemInputs, @ZenCodeType.Optional("[] as crafttweaker.api.fluid.FluidIngredient[]") CTFluidIngredient[] fluidInputs, @ZenCodeType.OptionalInt(100) int duration) {
+
+ name = fixRecipeName(name);
+ ResourceLocation resourceLocation = new ResourceLocation("crafttweaker", name);
+ ProcessingRecipeBuilder builder = new ProcessingRecipeBuilder<>(getSerializer().getFactory(), resourceLocation);
+ for(Percentaged output : outputs) {
+
+ builder.output((float) output.getPercentage(), output.getData().getInternal());
+ }
+
+ List ingredients = new ArrayList<>();
+ for(IIngredientWithAmount iIngredientWithAmount : itemInputs) {
+ for(int i = 0; i < iIngredientWithAmount.getAmount(); i++) {
+ ingredients.add(iIngredientWithAmount.getIngredient()
+ .asVanillaIngredient());
+ }
+ }
+ builder.withItemIngredients(ingredients.toArray(new Ingredient[0]));
+ if(fluidInputs != null && fluidInputs.length != 0) {
+ builder.withFluidIngredients(Arrays.stream(fluidInputs)
+ .map(CTDestroy::mapFluidIngredients)
+ .toArray(FluidIngredient[]::new));
+ }
+ builder.requiresHeat(heat);
+
+ builder.duration(duration);
+ CraftTweakerAPI.apply(new ActionAddRecipe<>(this, builder.build()));
+ }
+
+ /**
+ * Adds an electrolysis recipe that outputs FluidStacks.
+ *
+ * @param name The name of the recipe.
+ * @param heat The required heat of the recipe.
+ * @param outputs The output FluidStacks of the recipe.
+ * @param itemInputs The item inputs of the recipe.
+ * @param fluidInputs The optional fluid inputs of the recipe.
+ * @param duration The duration of the recipe in ticks.
+ *
+ * @docParam name "fluid_electrolysed"
+ * @docParam heat
+ * @docParam outputs [ * 200]
+ * @docParam itemInputs [ * 2]
+ * @docParam fluidInputs [ * 250]
+ * @docParam duration 200
+ */
+ @ZenCodeType.Method
+ public void addRecipe(String name, HeatCondition heat, IFluidStack[] outputs, IIngredientWithAmount[] itemInputs, @ZenCodeType.Optional("[] as crafttweaker.api.fluid.FluidIngredient[]") CTFluidIngredient[] fluidInputs, @ZenCodeType.OptionalInt(100) int duration) {
+
+ name = fixRecipeName(name);
+ ResourceLocation resourceLocation = new ResourceLocation("crafttweaker", name);
+ ProcessingRecipeBuilder builder = new ProcessingRecipeBuilder<>(getSerializer().getFactory(), resourceLocation);
+
+ for(IFluidStack output : outputs) {
+ CTDestroy.output(builder, output);
+ }
+
+ List ingredients = new ArrayList<>(itemInputs.length);
+ for(IIngredientWithAmount iIngredientWithAmount : itemInputs) {
+ for(int i = 0; i < iIngredientWithAmount.getAmount(); i++) {
+ ingredients.add(iIngredientWithAmount.getIngredient()
+ .asVanillaIngredient());
+ }
+ }
+ builder.withItemIngredients(ingredients.toArray(new Ingredient[0]));
+ if(fluidInputs != null) {
+ builder.withFluidIngredients(Arrays.stream(fluidInputs)
+ .map(CTDestroy::mapFluidIngredients)
+ .toArray(FluidIngredient[]::new));
+ }
+ builder.requiresHeat(heat);
+
+ builder.duration(duration);
+ CraftTweakerAPI.apply(new ActionAddRecipe<>(this, builder.build()));
+ }
+ @Override
+ public DestroyRecipeTypes getDestroyRecipeType() {
+ return DestroyRecipeTypes.ELECTROLYSIS;
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/ExtrusionRecipeManager.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/ExtrusionRecipeManager.java
new file mode 100644
index 000000000..297ead959
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/ExtrusionRecipeManager.java
@@ -0,0 +1,39 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.manager;
+
+import com.blamejared.crafttweaker.api.CraftTweakerAPI;
+import com.blamejared.crafttweaker.api.action.recipe.ActionAddRecipe;
+import com.blamejared.crafttweaker.api.ingredient.IIngredient;
+import com.blamejared.crafttweaker.api.item.IItemStack;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.petrolpark.destroy.recipe.ChargingRecipe;
+import com.petrolpark.destroy.recipe.DestroyRecipeTypes;
+import com.petrolpark.destroy.recipe.ExtrusionRecipe;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+import net.minecraft.resources.ResourceLocation;
+import org.openzen.zencode.java.ZenCodeType;
+
+public class ExtrusionRecipeManager implements IDestroyRecipeManager {
+ /**
+ * Adds a recipe to the extruder
+ * @param name Name of the recipe
+ * @param input Item to charge
+ * @param output Output item
+ *
+ * @docParam name "charge_iron"
+ * @docParam input
+ * @docParam output
+ */
+ @ZenCodeType.Method
+ public void addRecipe(String name, IIngredient input, IItemStack output) {
+ name = fixRecipeName(name);
+ ResourceLocation resourceLocation = new ResourceLocation("crafttweaker", name);
+ ProcessingRecipeBuilder builder = new ProcessingRecipeBuilder<>(getSerializer().getFactory(), resourceLocation);
+ builder.withItemIngredients(input.asVanillaIngredient());
+ builder.withItemOutputs(CTDestroy.mapItemStackToProcessingOutput(output));
+ CraftTweakerAPI.apply(new ActionAddRecipe<>(this, builder.build()));
+ }
+ @Override
+ public DestroyRecipeTypes getDestroyRecipeType() {
+ return DestroyRecipeTypes.EXTRUSION;
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/IDestroyRecipeManager.java b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/IDestroyRecipeManager.java
new file mode 100644
index 000000000..591549f00
--- /dev/null
+++ b/src/main/java/com/petrolpark/destroy/compat/crafttweaker/recipes/manager/IDestroyRecipeManager.java
@@ -0,0 +1,93 @@
+package com.petrolpark.destroy.compat.crafttweaker.recipes.manager;
+
+import com.blamejared.crafttweaker.api.CraftTweakerAPI;
+import com.blamejared.crafttweaker.api.action.recipe.ActionAddRecipe;
+import com.blamejared.crafttweaker.api.action.recipe.ActionRemoveRecipe;
+import com.blamejared.crafttweaker.api.annotation.ZenRegister;
+import com.blamejared.crafttweaker.api.fluid.CTFluidIngredient;
+import com.blamejared.crafttweaker.api.recipe.manager.base.IRecipeManager;
+import com.blamejared.crafttweaker_annotations.annotations.Document;
+import com.petrolpark.destroy.chemistry.Mixture;
+import com.petrolpark.destroy.chemistry.Molecule;
+import com.petrolpark.destroy.chemistry.ReadOnlyMixture;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
+import com.petrolpark.destroy.compat.jei.DestroyJEI;
+import com.petrolpark.destroy.recipe.DestroyRecipeTypes;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipe;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeBuilder;
+import com.simibubi.create.content.processing.recipe.ProcessingRecipeSerializer;
+import com.simibubi.create.foundation.fluid.FluidIngredient;
+import net.minecraft.core.NonNullList;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.Tag;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.item.crafting.RecipeType;
+import net.minecraftforge.fluids.FluidStack;
+import org.openzen.zencode.java.ZenCodeType;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.function.Consumer;
+
+@ZenRegister
+@ZenCodeType.Name("mods.destroy.IDestroyRecipeManager")
+@Document("mods/destroy/IDestroyRecipeManager")
+public interface IDestroyRecipeManager > extends IRecipeManager {
+ default ProcessingRecipeSerializer getSerializer() {
+ return getDestroyRecipeType().getSerializer();
+ }
+
+ /**
+ * Registers a recipe using a builder approach.
+ *
+ * @param name The name of the recipe.
+ * @param recipeBuilder The recipe builder.
+ */
+ @ZenCodeType.Method
+ default void registerRecipe(String name, Consumer> recipeBuilder) {
+ name = fixRecipeName(name);
+ ResourceLocation recipeId = new ResourceLocation("crafttweaker", name);
+ ProcessingRecipeBuilder builder = new ProcessingRecipeBuilder<>(getSerializer().getFactory(), recipeId);
+ recipeBuilder.accept(builder);
+ CraftTweakerAPI.apply(new ActionAddRecipe<>(this, builder.build(), ""));
+ }
+
+ @ZenCodeType.Method("isMixture")
+ default boolean checkMixture(CTFluidIngredient ingredient) {
+ if(!(ingredient instanceof CTFluidIngredient.FluidStackIngredient fluidStack)) {
+ return false;
+ }
+ CompoundTag internalTag = fluidStack.getMatchingStacks().get(0).getInternalTag();
+ if(internalTag == null) return false;
+ return internalTag.contains("MaximumConcentration") || internalTag.contains("Mixture");
+ }
+
+ /**
+ * Throws an exception if the ingredient is a mixture
+ * @param ingredient
+ */
+ @ZenCodeType.Method("checkMixture")
+ default void checkMixtureOrThrow(CTFluidIngredient ingredient) {
+ // check if the mixture is a fluid
+ if(checkMixture(ingredient)) {
+ throw new IllegalArgumentException("Input fluid cannot be a mixture");
+ }
+ }
+
+ @ZenCodeType.Method
+ default void removeRecipe(CTFluidIngredient input) {
+ FluidIngredient nativeInput = CTDestroy.mapFluidIngredients(input);
+ CraftTweakerAPI.apply(new ActionRemoveRecipe<>(this, iRecipe -> iRecipe.getFluidIngredients()
+ .stream()
+ .anyMatch(ingredient -> ingredient.equals(nativeInput))));
+ }
+
+ DestroyRecipeTypes getDestroyRecipeType();
+
+ @Override
+ default RecipeType getRecipeType() {
+ return getDestroyRecipeType().getType();
+ }
+}
diff --git a/src/main/java/com/petrolpark/destroy/compat/jei/DestroyJEI.java b/src/main/java/com/petrolpark/destroy/compat/jei/DestroyJEI.java
index af82e9d83..e7276e5d4 100644
--- a/src/main/java/com/petrolpark/destroy/compat/jei/DestroyJEI.java
+++ b/src/main/java/com/petrolpark/destroy/compat/jei/DestroyJEI.java
@@ -1,11 +1,6 @@
package com.petrolpark.destroy.compat.jei;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
+import java.util.*;
import java.util.function.Consumer;
import java.util.function.Supplier;
@@ -86,12 +81,12 @@ public class DestroyJEI implements IModPlugin {
* A map of Molecules to the Recipes in which they are inputs.
* This does not include {@link com.petrolpark.destroy.chemistry.Reaction Reactions}.
*/
- public static final Map>> MOLECULES_INPUT = new HashMap<>();
+ public static final Map>> MOLECULES_INPUT = new HashMap<>();
/**
* A map of Molecules to the Recipes in which they are outputs.
* This does not include {@link com.petrolpark.destroy.chemistry.Reaction Reactions}.
*/
- public static final Map>> MOLECULES_OUTPUT = new HashMap<>();
+ public static final Map>> MOLECULES_OUTPUT = new HashMap<>();
/**
* Whether Recipes have not yet been added to {@link DestroyJEI#MOLECULES_INPUT} and {@link DestroyJEI#MOLECULES_OUTPUT}.
*/
@@ -368,7 +363,7 @@ public CreateRecipeCategory build(String name, DestroyRecipeCategory.Factory<
return recipes;
};
- mezz.jei.api.recipe.RecipeType type = new mezz.jei.api.recipe.RecipeType(Destroy.asResource(name), recipeClass);
+ mezz.jei.api.recipe.RecipeType type = new mezz.jei.api.recipe.RecipeType<>(Destroy.asResource(name), recipeClass);
Info info = new Info(
type,
diff --git a/src/main/java/com/petrolpark/destroy/compat/jei/DestroyRecipeManagerPlugin.java b/src/main/java/com/petrolpark/destroy/compat/jei/DestroyRecipeManagerPlugin.java
index 7883921dd..266fb7d2d 100644
--- a/src/main/java/com/petrolpark/destroy/compat/jei/DestroyRecipeManagerPlugin.java
+++ b/src/main/java/com/petrolpark/destroy/compat/jei/DestroyRecipeManagerPlugin.java
@@ -1,9 +1,6 @@
package com.petrolpark.destroy.compat.jei;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Optional;
+import java.util.*;
import com.petrolpark.destroy.chemistry.Group;
import com.petrolpark.destroy.chemistry.Molecule;
@@ -72,9 +69,9 @@ public List getRecipes(IRecipeCategory recipeCategory, IFocus fo
molecules = ReadOnlyMixture.readNBT(ReadOnlyMixture::new, fluidStack.getOrCreateChildTag("Mixture")).getContents(true);
};
- molecules.forEach(molecule -> {
+ for(Molecule molecule : molecules) {
recipes.addAll(getRecipes(recipeCategory, helpers.getFocusFactory().createFocus(focus.getRole(), MoleculeJEIIngredient.TYPE, molecule)));
- });
+ }
});
@@ -102,7 +99,7 @@ else if (recipeCategory instanceof ReactionCategory) {
};
// Add non-Reaction Recipes
- List> recipeUses = DestroyJEI.MOLECULES_INPUT.get(molecule); // Recipes in which a Mixture containing this Molecule is required
+ Set> recipeUses = DestroyJEI.MOLECULES_INPUT.get(molecule); // Recipes in which a Mixture containing this Molecule is required
if (recipeUses != null) {
recipes.addAll(recipeUses.stream()
.filter(recipe -> recipe.getClass().equals(DestroyJEI.RECIPE_TYPES.get(recipeCategory.getRecipeType()))) // Check for Recipes that match this category
@@ -130,7 +127,7 @@ else if (recipeCategory instanceof ReactionCategory) molecule.getProductReaction
}); // This is an unchecked conversion but I think it's fine
// Add non-Reaction Recipes
- List> recipeProductions = DestroyJEI.MOLECULES_OUTPUT.get(molecule); // Recipes in which a Mixture containing this Molecule is produced
+ Set> recipeProductions = DestroyJEI.MOLECULES_OUTPUT.get(molecule); // Recipes in which a Mixture containing this Molecule is produced
if (recipeProductions != null) recipes.addAll(recipeProductions.stream()
.filter(recipe -> recipe.getClass().equals(DestroyJEI.RECIPE_TYPES.get(recipeCategory.getRecipeType()))) // Check for Recipes that match this category
.map(recipe -> (T) recipe) // Cast these Recipes (unchecked conversion, but should be okay as we just checked the class)
@@ -152,6 +149,5 @@ else if (recipeCategory instanceof ReactionCategory) molecule.getProductReaction
@Override
public List getRecipes(IRecipeCategory recipeCategory) {
return List.of();
-
};
};
diff --git a/src/main/java/com/petrolpark/destroy/compat/jei/category/ReactionCategory.java b/src/main/java/com/petrolpark/destroy/compat/jei/category/ReactionCategory.java
index a7fe58994..b08c27c91 100644
--- a/src/main/java/com/petrolpark/destroy/compat/jei/category/ReactionCategory.java
+++ b/src/main/java/com/petrolpark/destroy/compat/jei/category/ReactionCategory.java
@@ -88,7 +88,7 @@ public Collection getHoverableTexts(ReactionRecipe reci
protected String getTranslationKey(ReactionRecipe recipe) {
Reaction reaction = recipe.getReaction();
- return reaction.getNameSpace() + ".reaction." + reaction.getId();
+ return reaction.getNamespace() + ".reaction." + reaction.getId();
};
@Override
@@ -97,7 +97,7 @@ public Palette getPaletteForBoxes() {
};
private static void tooManyMoleculesWarning(boolean reactants, Reaction reaction) {
- Destroy.LOGGER.warn("Reaction '"+reaction.getFullId()+"' has too many " + (reactants ? "reactants" : "products") + " to fit on JEI.");
+ Destroy.LOGGER.warn("Reaction '"+reaction.getFullID()+"' has too many " + (reactants ? "reactants" : "products") + " to fit on JEI.");
};
@Override
diff --git a/src/main/java/com/petrolpark/destroy/events/DestroyCommonEvents.java b/src/main/java/com/petrolpark/destroy/events/DestroyCommonEvents.java
index 188cf34da..11900e442 100644
--- a/src/main/java/com/petrolpark/destroy/events/DestroyCommonEvents.java
+++ b/src/main/java/com/petrolpark/destroy/events/DestroyCommonEvents.java
@@ -593,6 +593,7 @@ public static void onRightClickBlock(PlayerInteractEvent.RightClickBlock event)
// Fill Test Tubes from any Fluid-containing block
if (stack.getItem() instanceof TestTubeItem && TestTubeItem.isEmpty(stack) && player.isCreative()) {
BlockEntity be = level.getBlockEntity(pos);
+ if(be == null) return; // fix a crash
if (!(be instanceof VatSideBlockEntity) && !(be instanceof VatControllerBlockEntity) && be.getCapability(ForgeCapabilities.FLUID_HANDLER, event.getFace()).map(handler -> {
FluidStack drained = handler.drain(200, FluidAction.SIMULATE);
if (DestroyFluids.isMixture(drained)) {
diff --git a/src/main/java/com/petrolpark/destroy/fluid/ingredient/ConcentrationRangeFluidIngredient.java b/src/main/java/com/petrolpark/destroy/fluid/ingredient/ConcentrationRangeFluidIngredient.java
index 04e4cefdc..b504ae958 100644
--- a/src/main/java/com/petrolpark/destroy/fluid/ingredient/ConcentrationRangeFluidIngredient.java
+++ b/src/main/java/com/petrolpark/destroy/fluid/ingredient/ConcentrationRangeFluidIngredient.java
@@ -1,6 +1,7 @@
package com.petrolpark.destroy.fluid.ingredient;
import java.text.DecimalFormat;
+import java.util.List;
import com.google.gson.JsonObject;
import com.petrolpark.destroy.chemistry.Molecule;
@@ -19,8 +20,8 @@ public abstract class ConcentrationRangeFluidIngredient getMoleculeParticipants() {
+ return List.of();
+ }
+
public float getTargetConcentration() {
return (minConcentration + maxConcentration) / 2f;
};
diff --git a/src/main/java/com/petrolpark/destroy/fluid/ingredient/IonFluidIngredient.java b/src/main/java/com/petrolpark/destroy/fluid/ingredient/IonFluidIngredient.java
index 2008b5842..0b67bce02 100644
--- a/src/main/java/com/petrolpark/destroy/fluid/ingredient/IonFluidIngredient.java
+++ b/src/main/java/com/petrolpark/destroy/fluid/ingredient/IonFluidIngredient.java
@@ -40,7 +40,7 @@ public List getExampleMixtures() {
return List.of(mixture);
};
- protected static class Type extends MoleculeFluidIngredient.Type {
+ public static class Type extends MoleculeFluidIngredient.Type {
@Override
public IonFluidIngredient getNew() {
diff --git a/src/main/java/com/petrolpark/destroy/fluid/ingredient/MoleculeFluidIngredient.java b/src/main/java/com/petrolpark/destroy/fluid/ingredient/MoleculeFluidIngredient.java
index 7607b2d47..f67ca1596 100644
--- a/src/main/java/com/petrolpark/destroy/fluid/ingredient/MoleculeFluidIngredient.java
+++ b/src/main/java/com/petrolpark/destroy/fluid/ingredient/MoleculeFluidIngredient.java
@@ -7,6 +7,7 @@
import com.petrolpark.destroy.chemistry.Mixture;
import com.petrolpark.destroy.chemistry.Molecule;
import com.petrolpark.destroy.chemistry.ReadOnlyMixture;
+import com.petrolpark.destroy.compat.crafttweaker.CTDestroy;
import com.petrolpark.destroy.config.DestroyAllConfigs;
import com.petrolpark.destroy.fluid.ingredient.mixturesubtype.MixtureFluidIngredientSubType;
import com.petrolpark.destroy.util.DestroyLang;
@@ -22,7 +23,7 @@ public class MoleculeFluidIngredient extends ConcentrationRangeFluidIngredient getType() {
@@ -40,6 +41,11 @@ protected boolean testMixture(Mixture mixture) {
return mixture.hasUsableMolecule(molecule, minConcentration, maxConcentration, null);
};
+ @Override
+ public List getMoleculeParticipants() {
+ return List.of(molecule);
+ }
+
@Override
protected void readInternal(FriendlyByteBuf buffer) {
super.readInternal(buffer);
@@ -69,12 +75,23 @@ public List getExampleMixtures() {
return List.of(getMixtureOfRightConcentration(molecule));
};
- protected static class Type extends MixtureFluidIngredientSubType {
+ public static class Type extends MixtureFluidIngredientSubType {
@Override
public MoleculeFluidIngredient getNew() {
return new MoleculeFluidIngredient();
- };
+ }
+
+ @Override
+ public MoleculeFluidIngredient fromNBT(CompoundTag tag, int amount) {
+ MoleculeFluidIngredient result = new MoleculeFluidIngredient();
+ result.molecule = Molecule.getMolecule(tag.getString("MoleculeRequired"));
+ result.minConcentration = tag.getFloat("MinimumConcentration");
+ result.maxConcentration = tag.getFloat("MaximumConcentration");
+ result.amountRequired = amount;
+ return result;
+ }
+
@Override
public String getMixtureFluidIngredientSubtype() {
diff --git a/src/main/java/com/petrolpark/destroy/fluid/ingredient/MoleculeTagFluidIngredient.java b/src/main/java/com/petrolpark/destroy/fluid/ingredient/MoleculeTagFluidIngredient.java
index 4a2befcc0..214a6f23b 100644
--- a/src/main/java/com/petrolpark/destroy/fluid/ingredient/MoleculeTagFluidIngredient.java
+++ b/src/main/java/com/petrolpark/destroy/fluid/ingredient/MoleculeTagFluidIngredient.java
@@ -23,7 +23,7 @@ public class MoleculeTagFluidIngredient extends ConcentrationRangeFluidIngredien
public static final Type TYPE = new Type();
- protected MoleculeTag tag;
+ public MoleculeTag tag;
public MoleculeTagFluidIngredient(MoleculeTag tag, float minConc, float maxConc) {
this.tag = tag;
@@ -82,13 +82,23 @@ public List getExampleMixtures() {
return MoleculeTag.MOLECULES_WITH_TAGS.get(tag).stream().map(this::getMixtureOfRightConcentration).toList();
};
- protected static class Type extends MixtureFluidIngredientSubType {
+ public static class Type extends MixtureFluidIngredientSubType {
@Override
public MoleculeTagFluidIngredient getNew() {
return new MoleculeTagFluidIngredient();
};
+ @Override
+ public MoleculeTagFluidIngredient fromNBT(CompoundTag tag, int amount) {
+ MoleculeTagFluidIngredient result = new MoleculeTagFluidIngredient();
+ result.tag = MoleculeTag.MOLECULE_TAGS.get(tag.getString("MoleculeTag"));
+ result.minConcentration = tag.getFloat("MinimumConcentration");
+ result.maxConcentration = tag.getFloat("MaximumConcentration");
+ result.amountRequired = amount;
+ return result;
+ }
+
@Override
public String getMixtureFluidIngredientSubtype() {
return "mixtureFluidWithTaggedMolecules";
diff --git a/src/main/java/com/petrolpark/destroy/fluid/ingredient/RefrigerantDummyFluidIngredient.java b/src/main/java/com/petrolpark/destroy/fluid/ingredient/RefrigerantDummyFluidIngredient.java
index 3d7ca9c44..b4d887b37 100644
--- a/src/main/java/com/petrolpark/destroy/fluid/ingredient/RefrigerantDummyFluidIngredient.java
+++ b/src/main/java/com/petrolpark/destroy/fluid/ingredient/RefrigerantDummyFluidIngredient.java
@@ -45,8 +45,7 @@ public String getMixtureFluidIngredientSubtype() {
@Override
public List getDescription(CompoundTag fluidTag) {
- List tooltip = new ArrayList<>();
- tooltip.addAll(TooltipHelper.cutStringTextComponent(DestroyLang.translate("tooltip.mixture_ingredient.molecule_tag_1").string(), Palette.GRAY_AND_WHITE));
+ List tooltip = new ArrayList<>(TooltipHelper.cutStringTextComponent(DestroyLang.translate("tooltip.mixture_ingredient.molecule_tag_1").string(), Palette.GRAY_AND_WHITE));
tooltip.add(DestroyMolecules.Tags.REFRIGERANT.getFormattedName());
tooltip.addAll(TooltipHelper.cutStringTextComponent(DestroyLang.translate("tooltip.mixture_ingredient.refrigerants").string(), Palette.GRAY_AND_WHITE));
return tooltip;
diff --git a/src/main/java/com/petrolpark/destroy/fluid/ingredient/SaltFluidIngredient.java b/src/main/java/com/petrolpark/destroy/fluid/ingredient/SaltFluidIngredient.java
index 3e069358d..6e43c5266 100644
--- a/src/main/java/com/petrolpark/destroy/fluid/ingredient/SaltFluidIngredient.java
+++ b/src/main/java/com/petrolpark/destroy/fluid/ingredient/SaltFluidIngredient.java
@@ -25,8 +25,8 @@ public class SaltFluidIngredient extends ConcentrationRangeFluidIngredient getType() {
@@ -42,7 +42,17 @@ public void addNBT(CompoundTag fluidTag) {
@Override
protected boolean testMixture(Mixture mixture) {
- return mixture.hasUsableMolecule(cation, minConcentration * cation.getCharge(), maxConcentration * cation.getCharge(), (molecule) -> molecule == anion) && mixture.hasUsableMolecule(anion, minConcentration * -anion.getCharge(), maxConcentration * -anion.getCharge(), (molecule) -> molecule == cation);
+ return mixture.hasUsableMolecule(
+ cation,
+ minConcentration * cation.getCharge(),
+ maxConcentration * cation.getCharge(),
+ (molecule) -> molecule == anion
+ ) && mixture.hasUsableMolecule(
+ anion,
+ minConcentration * -anion.getCharge(),
+ maxConcentration * -anion.getCharge(),
+ (molecule) -> molecule == cation
+ );
};
@Override
@@ -86,13 +96,29 @@ public List getExampleMixtures() {
return List.of(mixture);
};
- protected static class Type extends MixtureFluidIngredientSubType {
+ @Override
+ public List getMoleculeParticipants() {
+ return List.of(cation, anion);
+ }
+
+ public static class Type extends MixtureFluidIngredientSubType {
@Override
public SaltFluidIngredient getNew() {
return new SaltFluidIngredient();
};
+ @Override
+ public SaltFluidIngredient fromNBT(CompoundTag tag, int amount) {
+ SaltFluidIngredient result = new SaltFluidIngredient();
+ result.cation = Molecule.getMolecule(tag.getString("RequiredCation"));
+ result.anion = Molecule.getMolecule(tag.getString("RequiredAnion"));
+ result.minConcentration = tag.getFloat("MinimumConcentration");
+ result.maxConcentration = tag.getFloat("MaximumConcentration");
+ result.amountRequired = amount;
+ return result;
+ }
+
@Override
public String getMixtureFluidIngredientSubtype() {
return "mixtureFluidWithSalt";
diff --git a/src/main/java/com/petrolpark/destroy/fluid/ingredient/mixturesubtype/MixtureFluidIngredientSubType.java b/src/main/java/com/petrolpark/destroy/fluid/ingredient/mixturesubtype/MixtureFluidIngredientSubType.java
index 34904d295..48cd28bca 100644
--- a/src/main/java/com/petrolpark/destroy/fluid/ingredient/mixturesubtype/MixtureFluidIngredientSubType.java
+++ b/src/main/java/com/petrolpark/destroy/fluid/ingredient/mixturesubtype/MixtureFluidIngredientSubType.java
@@ -13,6 +13,13 @@ public abstract class MixtureFluidIngredientSubType cir) {
+ CompoundTag tag = fluidStack.getTag();
+ if(tag == null) return;
+ String subtypeName = tag.getString("MixtureFluidIngredientSubtype");
+ if(subtypeName.isEmpty()) return;
+ MixtureFluidIngredientSubType> subtype = MixtureFluidIngredient.MIXTURE_FLUID_INGREDIENT_SUBTYPES.get(subtypeName);
+ if(subtype == null) return;
+ cir.setReturnValue(subtype.fromNBT(tag, fluidStack.getAmount()));
+ }
};
diff --git a/src/main/java/com/petrolpark/destroy/mixin/HeatConditionMixin.java b/src/main/java/com/petrolpark/destroy/mixin/HeatConditionMixin.java
index ba4b182d5..677e22f31 100644
--- a/src/main/java/com/petrolpark/destroy/mixin/HeatConditionMixin.java
+++ b/src/main/java/com/petrolpark/destroy/mixin/HeatConditionMixin.java
@@ -57,7 +57,7 @@ public abstract class HeatConditionMixin {
public void inTestBlazeBurner(HeatLevel heatLevel, CallbackInfoReturnable ci) {
HeatCondition thisHeatCondition = (HeatCondition) (Object) (this);
if (thisHeatCondition == COOLED) {
- ci.setReturnValue(heatLevel.name() == "FROSTING");
+ ci.setReturnValue(heatLevel.name().equals("FROSTING"));
};
};
};
diff --git a/src/main/java/com/petrolpark/destroy/mixin/MechanicalMixerBlockEntityMixin.java b/src/main/java/com/petrolpark/destroy/mixin/MechanicalMixerBlockEntityMixin.java
index 9551be355..de3622e75 100644
--- a/src/main/java/com/petrolpark/destroy/mixin/MechanicalMixerBlockEntityMixin.java
+++ b/src/main/java/com/petrolpark/destroy/mixin/MechanicalMixerBlockEntityMixin.java
@@ -2,11 +2,20 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Optional;
+import com.simibubi.create.content.fluids.potion.PotionMixingRecipes;
+import com.simibubi.create.content.kinetics.mixer.MixingRecipe;
+import com.simibubi.create.content.processing.basin.BasinBlockEntity;
+import com.simibubi.create.content.processing.basin.BasinOperatingBlockEntity;
+import com.simibubi.create.infrastructure.config.AllConfigs;
+import net.minecraft.core.BlockPos;
+import net.minecraft.world.level.block.entity.BlockEntityType;
+import net.minecraft.world.level.block.state.BlockState;
import org.spongepowered.asm.mixin.Mixin;
-import org.spongepowered.asm.mixin.injection.At;
-import org.spongepowered.asm.mixin.injection.Inject;
-import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
+import org.spongepowered.asm.mixin.Overwrite;
+import org.spongepowered.asm.mixin.Shadow;
+import org.spongepowered.asm.mixin.Unique;
import com.petrolpark.destroy.fluid.DestroyFluids;
import com.petrolpark.destroy.mixin.accessor.BasinOperatingBlockEntityAccessor;
@@ -22,54 +31,100 @@
import net.minecraftforge.items.IItemHandler;
@Mixin(MechanicalMixerBlockEntity.class)
-public class MechanicalMixerBlockEntityMixin {
-
+public abstract class MechanicalMixerBlockEntityMixin extends BasinOperatingBlockEntity {
+
+ @Shadow public boolean running;
+
+ public MechanicalMixerBlockEntityMixin(BlockEntityType> typeIn, BlockPos pos, BlockState state) {
+ super(typeIn, pos, state);
+ }
+
/**
* Injection into {@link com.simibubi.create.content.contraptions.components.mixer.MechanicalMixerBlockEntity Mechanical Mixers}
* to allow them to recognise Mixtures that are able to React.
- * @see com.petrolpark.destroy.recipe.ReactionInBasinRecipe Reactions in Basins
+ *
+ * @return
+ * @see ReactionInBasinRecipe Reactions in Basins
+ * @author petrolpark
+ * @reason Properly handle recipes which involve mixtures
*/
- @Inject(
- method = "getMatchingRecipes()Ljava/util/List;",
- at = @At("HEAD"),
- cancellable = true,
- remap = false
-
- )
- public void inGetMatchingRecipes(CallbackInfoReturnable
>> ci) {
-
- ((BasinOperatingBlockEntityAccessor)this).invokeGetBasin().ifPresent(basin -> {
-
- if (!basin.hasLevel()) return;
-
- IFluidHandler fluidHandler = basin.getCapability(ForgeCapabilities.FLUID_HANDLER).orElse(null);
- IItemHandler itemHandler = basin.getCapability(ForgeCapabilities.ITEM_HANDLER).orElse(null);
- if (fluidHandler == null || itemHandler == null) return;
-
- boolean containsOnlyMixtures = true;
- List availableItemStacks = new ArrayList<>();
- List availableFluidStacks = new ArrayList<>(); // All (Mixture) Fluid Stacks in this Basin
-
- for (int tank = 0; tank < fluidHandler.getTanks(); tank++) {
- FluidStack fluidStack = fluidHandler.getFluidInTank(tank);
- if (DestroyFluids.isMixture(fluidStack)) {
- availableFluidStacks.add(fluidStack);
- } else if (!fluidStack.isEmpty()) {
- containsOnlyMixtures = false;
- };
- };
-
- if (!containsOnlyMixtures) return; // If there are Fluids other than Mixtures, don't bother Reacting
- if (availableFluidStacks.size() <= 0) return; // If there are no Mixtures, don't bother Reacting
-
- for (int slot = 0; slot < itemHandler.getSlots(); slot++) {
- availableItemStacks.add(itemHandler.getStackInSlot(slot));
- };
-
- // Only return this if there is definitely a Reaction possible
- ReactionInBasinRecipe recipe = ReactionInBasinRecipe.create(availableFluidStacks, availableItemStacks, basin);
- if (!(recipe == null) && BasinRecipe.match(basin, recipe)) ci.setReturnValue(List.of(recipe)); // It thinks basin.getLevel() might be null
- });
- };
-
-};
+ @Overwrite(remap = false)
+ public List> getMatchingRecipes() {
+ List> matchingRecipes = super.getMatchingRecipes();
+ List> basinReactions = destroy$getBasinReactionRecipes();
+ List> together = new ArrayList<>(matchingRecipes.size() + basinReactions.size());
+ together.addAll(matchingRecipes);
+ together.addAll(basinReactions);
+ if (!AllConfigs.server().recipes.allowBrewingInMixer.get())
+ return together;
+
+ Optional basin = getBasin();
+ if (basin.isEmpty())
+ return together;
+
+ BasinBlockEntity basinBlockEntity = basin.get();
+
+ IItemHandler availableItems = basinBlockEntity
+ .getCapability(ForgeCapabilities.ITEM_HANDLER)
+ .orElse(null);
+ if (availableItems == null) {
+ return together;
+ }
+
+ for (int i = 0; i < availableItems.getSlots(); i++) {
+ ItemStack stack = availableItems.getStackInSlot(i);
+ if (stack.isEmpty())
+ continue;
+
+ List list = PotionMixingRecipes.BY_ITEM.get(stack.getItem());
+ if (list == null)
+ continue;
+ for (MixingRecipe mixingRecipe : list)
+ if (matchBasinRecipe(mixingRecipe))
+ matchingRecipes.add(mixingRecipe);
+ }
+
+ return together;
+ }
+
+ @Unique
+ public List> destroy$getBasinReactionRecipes() {
+
+ BasinBlockEntity basin = ((BasinOperatingBlockEntityAccessor) this).invokeGetBasin().orElse(null);
+ if(basin == null) return List.of();
+ if (!basin.hasLevel()) return List.of();
+
+ // ignore IDE warnings here
+ IFluidHandler fluidHandler = basin.getCapability(ForgeCapabilities.FLUID_HANDLER).orElse(null);
+ IItemHandler itemHandler = basin.getCapability(ForgeCapabilities.ITEM_HANDLER).orElse(null);
+ if(fluidHandler == null || itemHandler == null) return List.of();
+ boolean containsOnlyMixtures = true;
+ List