From 092d50168bbb215ddd78eabbb61b3f2304e8cb13 Mon Sep 17 00:00:00 2001 From: kross <135918757+krossgg@users.noreply.github.com> Date: Wed, 18 Dec 2024 17:41:05 -0500 Subject: [PATCH] Move docs to main repo (#2583) --- .github/workflows/deploy-pages.yml | 47 +++ docs/.gitignore | 8 + docs/CONTRIBUTING.md | 144 +++++++ docs/LICENSE | 121 ++++++ docs/README.md | 24 ++ docs/content/.pages | 5 + docs/content/Development/.pages | 7 + .../content/Development/External-Resources.md | 67 +++ docs/content/Development/FAQ.md | 21 + .../General-Topics/Capabilities.md | 20 + .../Energy-Fluid-Item-Storage.md | 127 ++++++ .../Development/General-Topics/Global-Data.md | 41 ++ .../Handling-Fluid-Bucket-Size.md | 15 + .../General-Topics/Optimization.md | 8 + .../General-Topics/Ore-Generation.md | 116 ++++++ .../Overclocking-And-Parallel-Logic.md | 80 ++++ .../General-Topics/Tick-Updates.md | 171 ++++++++ docs/content/Development/Glossary.md | 62 +++ docs/content/Development/SyncData/.pages | 5 + .../Development/SyncData/Annotations/.pages | 6 + .../SyncData/Annotations/DescSynced.md | 6 + .../SyncData/Annotations/Persisted.md | 6 + .../SyncData/Annotations/RequireRerender.md | 6 + .../SyncData/Annotations/UpdateListener.md | 6 + .../Development/SyncData/Using-SyncData.md | 50 +++ docs/content/Development/SyncData/index.md | 12 + docs/content/Development/index.md | 22 + docs/content/Gameplay/Ore-Generation.md | 380 ++++++++++++++++++ docs/content/Gameplay/Steam/Bronze.md | 13 + .../Gameplay/Steam/Steam-Generation.md | 49 +++ .../Gameplay/Steam/assets/bronze_recipe.png | Bin 0 -> 1571 bytes .../Gameplay/Steam/assets/ingot_crushed.png | Bin 0 -> 3426 bytes .../Gameplay/Steam/assets/p_water_pump.png | Bin 0 -> 49042 bytes docs/content/Gameplay/Steam/index.md | 17 + docs/content/Gameplay/index.md | 6 + docs/content/Modpacks/.pages | 7 + docs/content/Modpacks/Beyond-The-Docs.md | 39 ++ docs/content/Modpacks/Changes/v1.1.0.md | 28 ++ docs/content/Modpacks/Changes/v1.2.1.md | 58 +++ docs/content/Modpacks/Examples/Alternator.md | 72 ++++ docs/content/Modpacks/Examples/Greenhouse.md | 218 ++++++++++ .../Modpacks/Examples/Ore-Processing-Plant.md | 62 +++ .../Modpacks/Examples/Parallel-Hatch-Part.md | 36 ++ .../Modpacks/Examples/Reducing-Duration.md | 27 ++ .../Modpacks/Examples/Removing-Ore-Piles.md | 23 ++ docs/content/Modpacks/Examples/index.md | 8 + .../01-Material-Creation.md | 130 ++++++ .../02-Element-Creation.md | 29 ++ .../Materials-and-Elements/Material-Flags.md | 67 +++ .../Material-Icon-Sets.md | 50 +++ .../Material-Properties.md | 45 +++ .../Modifying-Existing-Materials.md | 84 ++++ ...Prefixes-and-the-power-of-.setIgnored().md | 46 +++ .../The-ChemicalHelper-class.md | 44 ++ .../Modpacks/Materials-and-Elements/index.md | 14 + .../Ore-Generation/01-Customizing-Veins.md | 143 +++++++ .../Modpacks/Ore-Generation/02-Generators.md | 137 +++++++ .../Ore-Generation/03-Adding-Stone-Types.md | 74 ++++ .../04-Layers-and-Dimensions.md | 35 ++ .../Ore-Generation/Bedrock-Fluid-Veins.md | 27 ++ docs/content/Modpacks/Ore-Generation/index.md | 20 + .../Adding-and-Removing-Recipes.md | 143 +++++++ .../Modpacks/Other-Topics/Custom-Coils.md | 23 ++ .../Modpacks/Other-Topics/Custom-Machines.md | 134 ++++++ .../Other-Topics/Custom-Recipe-Types.md | 41 ++ .../Modpacks/Other-Topics/LDLib-UI-Editor.md | 10 + .../Modifying-Crafting-Components.md | 150 +++++++ docs/content/Modpacks/Other-Topics/index.md | 8 + docs/content/Modpacks/index.md | 38 ++ docs/content/assets/favicon.png | Bin 0 -> 839 bytes docs/content/assets/logo.png | Bin 0 -> 700 bytes docs/content/index.md | 28 ++ docs/content/stylesheets/extra.css | 48 +++ docs/mkdocs.yml | 80 ++++ docs/requirements.txt | 2 + 75 files changed, 3896 insertions(+) create mode 100644 .github/workflows/deploy-pages.yml create mode 100644 docs/.gitignore create mode 100644 docs/CONTRIBUTING.md create mode 100644 docs/LICENSE create mode 100644 docs/README.md create mode 100644 docs/content/.pages create mode 100644 docs/content/Development/.pages create mode 100644 docs/content/Development/External-Resources.md create mode 100644 docs/content/Development/FAQ.md create mode 100644 docs/content/Development/General-Topics/Capabilities.md create mode 100644 docs/content/Development/General-Topics/Energy-Fluid-Item-Storage.md create mode 100644 docs/content/Development/General-Topics/Global-Data.md create mode 100644 docs/content/Development/General-Topics/Handling-Fluid-Bucket-Size.md create mode 100644 docs/content/Development/General-Topics/Optimization.md create mode 100644 docs/content/Development/General-Topics/Ore-Generation.md create mode 100644 docs/content/Development/General-Topics/Overclocking-And-Parallel-Logic.md create mode 100644 docs/content/Development/General-Topics/Tick-Updates.md create mode 100644 docs/content/Development/Glossary.md create mode 100644 docs/content/Development/SyncData/.pages create mode 100644 docs/content/Development/SyncData/Annotations/.pages create mode 100644 docs/content/Development/SyncData/Annotations/DescSynced.md create mode 100644 docs/content/Development/SyncData/Annotations/Persisted.md create mode 100644 docs/content/Development/SyncData/Annotations/RequireRerender.md create mode 100644 docs/content/Development/SyncData/Annotations/UpdateListener.md create mode 100644 docs/content/Development/SyncData/Using-SyncData.md create mode 100644 docs/content/Development/SyncData/index.md create mode 100644 docs/content/Development/index.md create mode 100644 docs/content/Gameplay/Ore-Generation.md create mode 100644 docs/content/Gameplay/Steam/Bronze.md create mode 100644 docs/content/Gameplay/Steam/Steam-Generation.md create mode 100644 docs/content/Gameplay/Steam/assets/bronze_recipe.png create mode 100644 docs/content/Gameplay/Steam/assets/ingot_crushed.png create mode 100644 docs/content/Gameplay/Steam/assets/p_water_pump.png create mode 100644 docs/content/Gameplay/Steam/index.md create mode 100644 docs/content/Gameplay/index.md create mode 100644 docs/content/Modpacks/.pages create mode 100644 docs/content/Modpacks/Beyond-The-Docs.md create mode 100644 docs/content/Modpacks/Changes/v1.1.0.md create mode 100644 docs/content/Modpacks/Changes/v1.2.1.md create mode 100644 docs/content/Modpacks/Examples/Alternator.md create mode 100644 docs/content/Modpacks/Examples/Greenhouse.md create mode 100644 docs/content/Modpacks/Examples/Ore-Processing-Plant.md create mode 100644 docs/content/Modpacks/Examples/Parallel-Hatch-Part.md create mode 100644 docs/content/Modpacks/Examples/Reducing-Duration.md create mode 100644 docs/content/Modpacks/Examples/Removing-Ore-Piles.md create mode 100644 docs/content/Modpacks/Examples/index.md create mode 100644 docs/content/Modpacks/Materials-and-Elements/01-Material-Creation.md create mode 100644 docs/content/Modpacks/Materials-and-Elements/02-Element-Creation.md create mode 100644 docs/content/Modpacks/Materials-and-Elements/Material-Flags.md create mode 100644 docs/content/Modpacks/Materials-and-Elements/Material-Icon-Sets.md create mode 100644 docs/content/Modpacks/Materials-and-Elements/Material-Properties.md create mode 100644 docs/content/Modpacks/Materials-and-Elements/Modifying-Existing-Materials.md create mode 100644 docs/content/Modpacks/Materials-and-Elements/TagPrefixes-and-the-power-of-.setIgnored().md create mode 100644 docs/content/Modpacks/Materials-and-Elements/The-ChemicalHelper-class.md create mode 100644 docs/content/Modpacks/Materials-and-Elements/index.md create mode 100644 docs/content/Modpacks/Ore-Generation/01-Customizing-Veins.md create mode 100644 docs/content/Modpacks/Ore-Generation/02-Generators.md create mode 100644 docs/content/Modpacks/Ore-Generation/03-Adding-Stone-Types.md create mode 100644 docs/content/Modpacks/Ore-Generation/04-Layers-and-Dimensions.md create mode 100644 docs/content/Modpacks/Ore-Generation/Bedrock-Fluid-Veins.md create mode 100644 docs/content/Modpacks/Ore-Generation/index.md create mode 100644 docs/content/Modpacks/Other-Topics/Adding-and-Removing-Recipes.md create mode 100644 docs/content/Modpacks/Other-Topics/Custom-Coils.md create mode 100644 docs/content/Modpacks/Other-Topics/Custom-Machines.md create mode 100644 docs/content/Modpacks/Other-Topics/Custom-Recipe-Types.md create mode 100644 docs/content/Modpacks/Other-Topics/LDLib-UI-Editor.md create mode 100644 docs/content/Modpacks/Other-Topics/Modifying-Crafting-Components.md create mode 100644 docs/content/Modpacks/Other-Topics/index.md create mode 100644 docs/content/Modpacks/index.md create mode 100644 docs/content/assets/favicon.png create mode 100644 docs/content/assets/logo.png create mode 100644 docs/content/index.md create mode 100644 docs/content/stylesheets/extra.css create mode 100644 docs/mkdocs.yml create mode 100644 docs/requirements.txt diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml new file mode 100644 index 0000000000..315c9cceac --- /dev/null +++ b/.github/workflows/deploy-pages.yml @@ -0,0 +1,47 @@ +name: Publish docs via GitHub Pages +on: + workflow_dispatch: + push: + branches: [1.20.1] + paths: ['docs/**'] + +jobs: + build: + name: build docs + runs-on: ubuntu-latest + defaults: + run: + working-directory: './docs' + steps: + - uses: actions/checkout@v4 + with: + ref: '1.20.1' + sparse-checkout: './docs' + - uses: actions/setup-python@v4 + with: + python-version: '3.11' + cache: 'pip' + - run: pip install -r ./requirements.txt + - uses: actions/cache@v4 + with: + key: 'mkdocs-cache' + path: './docs/.cache' + - name: Build static files + id: mkdocs + run: mkdocs build + - name: Upload pages as artifact + id: artifact + uses: actions/upload-pages-artifact@v3 + with: + path: './docs/site/' + + deploy: + runs-on: ubuntu-latest + needs: build + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000000..24c33be7ea --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,8 @@ +env/ +venv/ +.env +.venv +[Ii]nclude +[Ll]ib +[Ss]cripts +site diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 0000000000..b9cb088af9 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,144 @@ +# Contributor Info / Styleguide + +For consistency, please follow the following styleguide when submitting PRs. + + +## Filenames & Basic Page Structure + +The file structure and basic page layout must follow these conventions: + +- For each section, create a directory with a short, descriptive name in `Title Case`. +- File names should only contain `A-Z`, `a-z`, `0-9`, `-` and `_` (no spaces). +- Directory names must use dashes (`-`) instead of spaces. +- If you want to add a short description of what a section is about, include an `index.md` in its directory. +- Every page of the docs must include a Markdown front matter block, containing _at least_ its title, followed by + two empty lines. +- Every page must start with a level-1 heading. No other level-1 headings may be added. + +Note that the page name/title must be as short and descriptive as possible, as it's displayed in the navigation area. +You can, however, choose a longer version for the page _header_: + +```markdown +--- +title: My Page Name +--- + + +# This is my Custom Page Header! + +Page content here... +``` + +Contrary to filenames, both the title and header are **not** limited to specific characters. + + +## Empty Lines + +Please consider the following conventions for empty lines: + +- At least two empty lines before each heading +- Exactly one line after each heading +- Separate each "element" of a page by an empty line inbetween + (code blocks, lists, [admonitions](https://squidfunk.github.io/mkdocs-material/reference/admonitions/), etc.) + + +## Formatting + +Please use the following formatting rules for standard markdown: + +- Use double asterisks for bold text (`**bold**`) and underscores for italics (`_italics_`) +- Each code block must include a language whenever possible, so that syntax highlighting is applied correctly + + +## Index Pages + +Only use index pages for a short description of what the current section is about. + +Do not include any important documentation on index pages! +You may link to certain relevant pages (either inside the current section, or in other sections) though. + + +## Admonitions + +Use [admonitions](https://squidfunk.github.io/mkdocs-material/reference/admonitions/) in the following situations: + +- If you want to provide potentially useful, additional info about a feature, use an `info` admonition. +- For "see also" references, use an `info inline end` admonition. +- For tips / recommendations on _when_ or _how_ to use a feature, use he `tip` admonition type. + - For general notes on _when_ or _how_ to use a specific feature (in development related docs), use the `note` type. +- For warning about possible bugs or unwanted behavior, use the `warning` type. +- If a feature may result in destructive actions in certain scenarios, use the `danger` type. +- For providing code examples, use the `example` type. + - **Always** provide a title for code examples, even if it's simply "Example usage". + - Use collapsible blocks (`??? example "my example title"` instead of `!!!`) for code examples where possible. + _In some situations this might not be as useful as using a non-collapsible block. Just fall back to `!!!` in these cases._ +- For pages or sections that are not documented yet, use `!!! failure "Not yet documented"` +- For links, use the (custom) `link` type. + +We suggest adding an empty line after the title for consistency, like in the following example. +It is **strongly recommended** to include a custom, descriptive title for your admonition, if possible. + + +```markdown +!!! note "My descriptive title" + + This is my note's content +``` + + +### Admonitions for experimental or not-yet-merged features + +If a feature is either not yet merged or simply not stable yet, please add a title-only `warning inline end` before its +description, as shown below. + +For additional info (e.g. which branch the feature is currently in), add a `
` tag in the title, followed by the +additional info in italics. +If your additional information needs to be more detailed than that, consider using a collapsible admonition instead. + +```markdown +!!! warning inline end "Not merged yet.
_Branch: `my-branch-name`_" + +Paragraph containing the feature's description... +``` + +Here are some good examples you could use as the first line of your warning: + +- Experimental +- Unstable +- Not yet implemented +- Not merged yet + + +## Annotations + +Use [annotations](https://squidfunk.github.io/mkdocs-material/reference/annotations/) in the following situations: + +- Explanatory comments in code examples (preferred over including these as an actual comment) +- Additional explanations to a specific aspect of your documented feature +- An explanation for a technical term used in your documentation + +There must be an empty line before the first annotation or they won't work properly. + +Also add an empty line between each annotation if at least one in the current block is longer than a single line. +In that case, add two empty lines after the end of the annotation list. + +Annotations must be numbered starting at 1 **for each block** of consecutive annotations. +If they are separated by another element, you need to start at 1 again for the next block of annotations: + + +~~~markdown +```java +someVar.someMethod(); // (1) +return someVar; // (2) +``` + +1. annotation 1 for the first code block +2. annotation 2 for the first code block + + +```java +someVar.anotherMethod(); // (1) +``` + +1. annotation 1 for the second code block +~~~ diff --git a/docs/LICENSE b/docs/LICENSE new file mode 100644 index 0000000000..0e259d42c9 --- /dev/null +++ b/docs/LICENSE @@ -0,0 +1,121 @@ +Creative Commons Legal Code + +CC0 1.0 Universal + + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED + HEREUNDER. + +Statement of Purpose + +The laws of most jurisdictions throughout the world automatically confer +exclusive Copyright and Related Rights (defined below) upon the creator +and subsequent owner(s) (each and all, an "owner") of an original work of +authorship and/or a database (each, a "Work"). + +Certain owners wish to permanently relinquish those rights to a Work for +the purpose of contributing to a commons of creative, cultural and +scientific works ("Commons") that the public can reliably and without fear +of later claims of infringement build upon, modify, incorporate in other +works, reuse and redistribute as freely as possible in any form whatsoever +and for any purposes, including without limitation commercial purposes. +These owners may contribute to the Commons to promote the ideal of a free +culture and the further production of creative, cultural and scientific +works, or to gain reputation or greater distribution for their Work in +part through the use and efforts of others. + +For these and/or other purposes and motivations, and without any +expectation of additional consideration or compensation, the person +associating CC0 with a Work (the "Affirmer"), to the extent that he or she +is an owner of Copyright and Related Rights in the Work, voluntarily +elects to apply CC0 to the Work and publicly distribute the Work under its +terms, with knowledge of his or her Copyright and Related Rights in the +Work and the meaning and intended legal effect of CC0 on those rights. + +1. Copyright and Related Rights. A Work made available under CC0 may be +protected by copyright and related or neighboring rights ("Copyright and +Related Rights"). Copyright and Related Rights include, but are not +limited to, the following: + + i. the right to reproduce, adapt, distribute, perform, display, + communicate, and translate a Work; + ii. moral rights retained by the original author(s) and/or performer(s); +iii. publicity and privacy rights pertaining to a person's image or + likeness depicted in a Work; + iv. rights protecting against unfair competition in regards to a Work, + subject to the limitations in paragraph 4(a), below; + v. rights protecting the extraction, dissemination, use and reuse of data + in a Work; + vi. database rights (such as those arising under Directive 96/9/EC of the + European Parliament and of the Council of 11 March 1996 on the legal + protection of databases, and under any national implementation + thereof, including any amended or successor version of such + directive); and +vii. other similar, equivalent or corresponding rights throughout the + world based on applicable law or treaty, and any national + implementations thereof. + +2. Waiver. To the greatest extent permitted by, but not in contravention +of, applicable law, Affirmer hereby overtly, fully, permanently, +irrevocably and unconditionally waives, abandons, and surrenders all of +Affirmer's Copyright and Related Rights and associated claims and causes +of action, whether now known or unknown (including existing as well as +future claims and causes of action), in the Work (i) in all territories +worldwide, (ii) for the maximum duration provided by applicable law or +treaty (including future time extensions), (iii) in any current or future +medium and for any number of copies, and (iv) for any purpose whatsoever, +including without limitation commercial, advertising or promotional +purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each +member of the public at large and to the detriment of Affirmer's heirs and +successors, fully intending that such Waiver shall not be subject to +revocation, rescission, cancellation, termination, or any other legal or +equitable action to disrupt the quiet enjoyment of the Work by the public +as contemplated by Affirmer's express Statement of Purpose. + +3. Public License Fallback. Should any part of the Waiver for any reason +be judged legally invalid or ineffective under applicable law, then the +Waiver shall be preserved to the maximum extent permitted taking into +account Affirmer's express Statement of Purpose. In addition, to the +extent the Waiver is so judged Affirmer hereby grants to each affected +person a royalty-free, non transferable, non sublicensable, non exclusive, +irrevocable and unconditional license to exercise Affirmer's Copyright and +Related Rights in the Work (i) in all territories worldwide, (ii) for the +maximum duration provided by applicable law or treaty (including future +time extensions), (iii) in any current or future medium and for any number +of copies, and (iv) for any purpose whatsoever, including without +limitation commercial, advertising or promotional purposes (the +"License"). The License shall be deemed effective as of the date CC0 was +applied by Affirmer to the Work. Should any part of the License for any +reason be judged legally invalid or ineffective under applicable law, such +partial invalidity or ineffectiveness shall not invalidate the remainder +of the License, and in such case Affirmer hereby affirms that he or she +will not (i) exercise any of his or her remaining Copyright and Related +Rights in the Work or (ii) assert any associated claims and causes of +action with respect to the Work, in either case contrary to Affirmer's +express Statement of Purpose. + +4. Limitations and Disclaimers. + + a. No trademark or patent rights held by Affirmer are waived, abandoned, + surrendered, licensed or otherwise affected by this document. + b. Affirmer offers the Work as-is and makes no representations or + warranties of any kind concerning the Work, express, implied, + statutory or otherwise, including without limitation warranties of + title, merchantability, fitness for a particular purpose, non + infringement, or the absence of latent or other defects, accuracy, or + the present or absence of errors, whether or not discoverable, all to + the greatest extent permissible under applicable law. + c. Affirmer disclaims responsibility for clearing rights of other persons + that may apply to the Work or any use thereof, including without + limitation any person's Copyright and Related Rights in the Work. + Further, Affirmer disclaims responsibility for obtaining any necessary + consents, permissions or other rights required for any use of the + Work. + d. Affirmer understands and acknowledges that Creative Commons is not a + party to this document and has no duty or obligation with respect to + this CC0 or use of the Work. diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000000..a023b8a3ee --- /dev/null +++ b/docs/README.md @@ -0,0 +1,24 @@ +# GregTech CEu Modern Documentation + +This documentation project is built using [MkDocs](https://www.mkdocs.org/#). + +For an automatically updating live preview in your browser, run `mkdocs serve` + + +## Installing Required Dependencies + +To install the required dependencies, please run `pip install -r requirements.txt` + + +## Used MkDocs Plugins + +The following plugins for MkDocs are being used: + +- https://squidfunk.github.io/mkdocs-material/ +- https://github.com/lukasgeiter/mkdocs-awesome-pages-plugin + +## Contributing + +When contributing to this project, please refer to the [Styleguide](./CONTRIBUTING.md). +I know it's kinda long, but it will help in keeping the documentation consistent. You will also find examples of +when to use specific features of mkdocs and mkdocs-material. diff --git a/docs/content/.pages b/docs/content/.pages new file mode 100644 index 0000000000..bb2adb55ff --- /dev/null +++ b/docs/content/.pages @@ -0,0 +1,5 @@ +nav: + - 'Home': 'index.md' + - 'Gameplay' + - 'Modpack Authors': Modpacks + - 'Developers': 'Development' \ No newline at end of file diff --git a/docs/content/Development/.pages b/docs/content/Development/.pages new file mode 100644 index 0000000000..af6db0ddec --- /dev/null +++ b/docs/content/Development/.pages @@ -0,0 +1,7 @@ +nav: + - index.md + - Glossary.md + - External-Resources.md + - FAQ.md + - General-Topics + - ... \ No newline at end of file diff --git a/docs/content/Development/External-Resources.md b/docs/content/Development/External-Resources.md new file mode 100644 index 0000000000..434eaf4d35 --- /dev/null +++ b/docs/content/Development/External-Resources.md @@ -0,0 +1,67 @@ +--- +icon: "material/link-box-variant" +title: "External Resources" +--- + + +# :material-link-box-variant: External Resources + +This page contains links to external documentation that may be useful for several topics regarding development, as well +as other resources you might find helpful. + + +## General Modding Docs + +!!! link "Official Forge / NeoForge Docs" + - [Forge-Wiki](https://docs.minecraftforge.net/en/1.20.x/) + - [NeoForge-Wiki](https://docs.neoforged.net/) + +!!! link "Official Fabric Docs" + [Fabric-Wiki :material-arrow-right: Mod Developer Docs](https://fabricmc.net/wiki/tutorial:start) + +!!! link "Other Modding Docs" + - [Forge Community-Wiki](https://forge.gemwire.uk/wiki/Main_Page) + - [MCJty's Modding Wiki](https://www.mcjty.eu/docs/intro) + + +## LDLib + +LDLib is the main library we're using for GTCEu-Modern. + +!!! link "LDLib Docs" + [:material-github: LDLib-Architectury :material-arrow-right: Wiki](https://github.com/Low-Drag-MC/LDLib-Architectury/wiki) + + +## Mixins + +!!! link "Overview on using Mixins" + [Fabric-Wiki :material-arrow-right: Mixins](https://fabricmc.net/wiki/tutorial:mixin_introduction) + + This is a great resource for getting started on how to use mixins, as well as a good quick reference if you're + looking for how to do something specific. + + Note that this is not exclusive to Fabric, but applies for all platforms instead. + +!!! link "Official Docs" + [:material-github: Mixin :material-arrow-right: Wiki](https://github.com/SpongePowered/Mixin/wiki) + + A more detailed technical documentation on mixins. + + +## DFU (DataFixerUpper) + +!!! link "Unofficial Documentation" + [:material-github: Documented DataFixerUpper](https://github.com/kvverti/Documented-DataFixerUpper) + + Unofficial documentation for Mojang's DataFixerUpper library. + + +## Registrate & Architectury + +!!! link "Using Registrate" + [:material-github: Registrate](https://github.com/tterrag1098/Registrate) + +!!! link "Architectury Gradle Plugin" + [Architectury-Wiki :material-arrow-right: Gradle Plugin](https://docs.architectury.dev/plugin/introduction) + + Note that we're only using Architectury's build system, not the library itself. \ No newline at end of file diff --git a/docs/content/Development/FAQ.md b/docs/content/Development/FAQ.md new file mode 100644 index 0000000000..519082ed9d --- /dev/null +++ b/docs/content/Development/FAQ.md @@ -0,0 +1,21 @@ +--- +icon: "material/frequently-asked-questions" +title: "FAQ" +--- + + +# :material-frequently-asked-questions: Frequently Asked Questions + + +## How to disable Shimmer's colored light monitor + +Shimmer (one of our soft-dependencies) displays information about colored lights in development environments by default. +You can turn it off using the command `/shimmer coloredLightMonitor` ingame. + +Alternatively, if you want to disable it by default, put this in your KubeJS client scripts: + +```js +// Disable Shimmer's colored light monitor: +let LightCounterRender = Java.loadClass("com.lowdragmc.shimmer.client.light.LightCounter$Render") +LightCounterRender.enable = false +``` diff --git a/docs/content/Development/General-Topics/Capabilities.md b/docs/content/Development/General-Topics/Capabilities.md new file mode 100644 index 0000000000..07de9ae565 --- /dev/null +++ b/docs/content/Development/General-Topics/Capabilities.md @@ -0,0 +1,20 @@ +--- +title: Capabilities +--- + + +# How to access Capabilities of blocks and items + +Fabric doesn't have a capability system like Forge does, but you can use several utility methods instead: + +```java +FluidTransferHelper.getFluidTransfer(...); +ItemTransferHelper.getItemTransfer(...); +GTCapabilityHelper.getRecipeLogic(...) +GTCapabilityHelper.getControllable(...) +GTCapabilityHelper.getCoverable(...) +GTCapabilityHelper.getToolable(...) +GTCapabilityHelper.getWorkable(...) +GTCapabilityHelper.getElectricItem(...) +GTCapabilityHelper.getEnergyContainer(...) +``` diff --git a/docs/content/Development/General-Topics/Energy-Fluid-Item-Storage.md b/docs/content/Development/General-Topics/Energy-Fluid-Item-Storage.md new file mode 100644 index 0000000000..b478d3a3f1 --- /dev/null +++ b/docs/content/Development/General-Topics/Energy-Fluid-Item-Storage.md @@ -0,0 +1,127 @@ +--- +title: Item/Fluid/Energy Storage +--- + + +# How to add an Item Inventory / Fluid Storage / Energy Container + +!!! note + + In general, these containers should be created as `final` fields (that's what we need for the + [SyncData](../SyncData/index.md) system). + Set their base arguments in the constructor (you can pass args for subclasses to modify). + + +## Implementations for Recipe Processing and adding Capabilities + +You can create these containers via one of the following classes: + +- `NotifiableItemStackHandler` +- `NotifiableFluidTank` +- `NotifiableEnergyContainer` + +In general, you should prefer these classes over other implementations if possible, as they notify all listeners +of internal changes to improve performance. + +**IO constructor parameters:** + +- `handlerIO`: Whether the container is regarded as input or output during recipe processing +- `capabilityIO`: Whether the player can use hoppers, pipes, cables, etc. to interact with the storage + + +## General-Purpose implementations + +If you don't need to use the storage for recipe processing and/or providing capabilities, you can just use one of the +following classes, as they are more lightweight: + +- `ItemStackTransfer` +- `FluidStorage` + + +## Custom implementations + +In some cases, you might need to create a custom implementation for either of these containers. +To do so, use either of the following interfaces: + +- `IItemTransfer` +- `IFluidTransfer` +- `IEnergyContainer` + + +## Specialized proxy implementations + +In case you have special requirements to your containers, you may be able to use one of these implementations in +conjunction with one or more regular containers. +They generally act as a proxy to the underlying container(s), while also handling these requirements. + + +### Proxying multiple containers + +- `ItemTransferList` +- `FluidTransferList` +- `EnergyContainerList` + + +### IO-specific container proxies + +For proxying multiple containers, limited to a specific IO direction. + +- `IOItemTransferList` +- `IOFluidTransferList` + + +### Rate-Limited proxies + +!!! warning inline end "Not merged yet
_Branch: `mi-ender-link`_" + +If you need to proxy any item or fluid container that needs to be rate limited for insertion and extraction, you can +use either of the following classes: + +- `LimitingItemTransferProxy` +- `LimitingFluidTransferProxy` + +The transfer limit passed as a constructor parameter will not renew automatically. Your container will therefore stop +transferring anything once this limit is reached. + +If you want to make this a rate limit instead, you will have to schedule a task that regularly resets the transfer +limit to the maximum value for your task's interval: + +??? example "Example Usage" + + ```java + public class MyCover extends CoverBehavior { + private LimitingFluidTransferProxy transferProxy; + private ConditionalSubscriptionHandler rateLimitSubscription; + + public MyCover(IFluidTransfer myFluidTransfer) { + super(/* ... */); + + transferProxy = new LimitingFluidTransferProxy( + myFluidTransfer, + 0L // Initial limit of 0, will be updated regularly in isRateLimitRefreshActive() + ); + rateLimitSubscription = new ConditionalSubscriptionHandler( + this, + this::resetTransferRateLimit, + this::isRateLimitRefreshActive + ); + } + + @Override + public void onLoad() { + super.onLoad(); + rateLimitSubscription.initialize(coverHolder.getLevel()); + } + + private void resetTransferRateLimit() { + if (transferProxy == null) + return; + + transferProxy.setRemainingTransfer(transferRate.getMilliBuckets() * 20); + } + + private boolean isRateLimitRefreshActive() { + // ... + } + } + ``` \ No newline at end of file diff --git a/docs/content/Development/General-Topics/Global-Data.md b/docs/content/Development/General-Topics/Global-Data.md new file mode 100644 index 0000000000..8442155303 --- /dev/null +++ b/docs/content/Development/General-Topics/Global-Data.md @@ -0,0 +1,41 @@ +--- +title: Global Caches / Data +--- + + +# Storing Data Globally + +In certain cases (e.g. in a cache that holds all currently loaded instances of a machine), you might need to store data +in a global (static and mutable) variable. + +When doing so, you need to ensure that remote and serverside instances don't get mixed up. + + +## Using `SideLocal` + +!!! warning inline end "Not yet merged
_Branch: `mi-ender-link`_" + +To make working with this requirement easier, You can use `SideLocal` to store your global data. +It is similar to Java's `ThreadLocal`, but operates on the game's sides instead. + +If you are currently on the remote side (`LDLib.isRemote()` / on the client's `main` thread), it will return the +remote side's instance of your data. Otherwise, you will get the server side's instance. + +??? example "Example Usage" + + ```java + public class MyCache { + private static SideLocal> cache = new SideLocal<>(HashMap::new); + + public static void cacheData(UUID id, MyData data) { + cache.get().put(id, data); + } + + public static MyData getData(UUID id) { + return cache.get().get(id); + } + } + ``` + + Alternatively to passing an initializer for both instances to `SideLocal`'s constructor, you can also supply + separate instances for the remote and server side. \ No newline at end of file diff --git a/docs/content/Development/General-Topics/Handling-Fluid-Bucket-Size.md b/docs/content/Development/General-Topics/Handling-Fluid-Bucket-Size.md new file mode 100644 index 0000000000..07cad3fd9a --- /dev/null +++ b/docs/content/Development/General-Topics/Handling-Fluid-Bucket-Size.md @@ -0,0 +1,15 @@ +--- +title: Handling Fluid Bucket Size +--- + + +# Dealing with Fluid Bucket Sizes + +The fluid systems of Forge and Fabric use different units. +Make sure you use the correct units whenever you're handling fluid amounts. + +To get the size of one bucket, use the following method: + +```java +FluidHelper.getBucket(); // returns 1000 on Forge and 81000 on Fabric. +``` \ No newline at end of file diff --git a/docs/content/Development/General-Topics/Optimization.md b/docs/content/Development/General-Topics/Optimization.md new file mode 100644 index 0000000000..34056d3e8f --- /dev/null +++ b/docs/content/Development/General-Topics/Optimization.md @@ -0,0 +1,8 @@ +--- +title: Optimization +--- + + +# Optimization Techniques + +!!! failure "Not yet documented" \ No newline at end of file diff --git a/docs/content/Development/General-Topics/Ore-Generation.md b/docs/content/Development/General-Topics/Ore-Generation.md new file mode 100644 index 0000000000..0e686cdfc2 --- /dev/null +++ b/docs/content/Development/General-Topics/Ore-Generation.md @@ -0,0 +1,116 @@ +--- +title: Ore Generation +--- + + +# Ore Generation + +Due to Minecraft's worldgen limitations (1), GTCEu's ore vein generation does not use the native worldgen feature system. +Instead, we have our own system of generating ore veins separately from the actual ore placement, +so that ores are only ever placed for the currently generating chunk. +This page roughly describes the process of generating, caching and placing ores. +{ .annotate } + +1. In Minecraft, worldgen features are only able to generate in a 3x3 chunk area, centered on the feature's origin chunk. + Because GTCEu introduces veins that may be larger than that (and have a random offset additionally), + the ore generation would exceed the allowed area in certain situations, causing the server thread to freeze/deadlock. + + +The generation can be (roughly) split up into three steps: + +- Vein Generation +- Generated Vein Caching +- Ore Placement (during chunk generation) + +This document will cover these steps from the bottom up, starting at the chunk generation mixin (`ChunkGeneratorMixin.gtceu$applyBiomeDecoration()`). + + +## Chunk Generation & Ore Placement + +The `ChunkGeneratorMixin` holds a reference to the `OrePlacer` (not to be confused with `OreBlockPlacer`) - which is used to place the +generated veins' blocks into the world, limited to the currently generating chunk. + + +## Generated Vein Caching + +When trying to generate a chunk, the `OrePlacer` will query the `OreGenCache` for a list of veins surrounding the current chunk. + +The radius for querying the surrounding area is determined by the `oreVeinRandomOffset` config option, as well as the largest registered vein size. +It is therefore automatically compatible with any additional (or changed default) veins registered through either KubeJS, or by an addon. + +Of course, the ore gen cache can only hold a limited amount of generated veins at once (see the `oreGenerationChunkCacheSize` config option). + + +### Randomness + +Because veins may be removed from the cache before all of their chunks are generated, it is **extremely important** that the ore generation is fully deterministic! + +This ensures that we do not generate ore veins that are either cut off, or have a mismatch in shape or type across chunk borders. +It also automatically applies across game restarts, keeping continuity even then. + +The only situation where ore veins will differ across chunk borders (other than certain internal changes to the generation, of course), is after +the relevant config options have been changed. + +In our case, that means that the `RandomSource`s used for world generation must be completely new for generating each vein, so that its type, shape, offset, +contents, etc. are not influenced by previous queries to the random generator. +It is completely and exclusively seeded from the world's seed, as well as the chunk position. + +For the random ore vein offset, we also include the vein's world generation layer in the random seed. +This may need to include an additional component in the future, in case we add support for multiple veins per chunk and worldgen-layer. + + +## Vein Generation + +Whenever the `OreGenCache` cannot find a vein for a specific chunk, it will request a list of that chunk's `GeneratedVein`s from the `OreGenerator`. + +The `OreGenerator` is responsible for determining a vein's type, its origin (influenced by the `oreVeinRandomOffset` config option), as well as providing the appropriate +randomness source to the used implementation of `VeinGenerator`. + +!!! info "Vein Origin vs Center" + + A vein's origin is always the chunk it originates in, regardless of the random offset. + The actual center of a vein **is** influenced by the random offset and might not be located at the chunk center - or in the same chunk at all. + +Once the relevant `VeinGenerator` implementation has finished generating the vein's shape, it will be cached per chunk, inside a `GeneratedVein`. + + +### `VeinGenerator` and `OreBlockPlacer` + +A vein generator is what will generate the actual shape of the vein. + +It should, however, never try to place any blocks directly. Instead, its `generate()` method will only return a map of `OreBlockPlacer`s by block position, which +are responsible for actually placing the blocks in the world, as soon as a chunk generates. +Each `OreBlockPlacer` should only place either a single block, or no block. + + +### Using Randomness in `OreBlockPlacer`s + +In certain situations, the process of actually placing the block requires a randomness source (e.g. to determine the chance of its block being placed). + +To keep the ore generation fully deterministic in this case as well, it is recommended to generate a new seed using the supplied `RandomSource` at the time of +vein shape generation. This seed should be passed into the `OreBlockPlacer` returned for the each block position. + +Inside the `OreBlockPlacer`, you can then simply create a new `RandomSource` using the precomputed seed. + +??? example "Using Randomness in an OreBlockPlacer" + + ```java + public class MyVeinGenerator { + public Map generate(WorldGenLevel level, RandomSource random, GTOreDefinition entry, BlockPos origin) { + Map generatedBlocks = new Object2ObjectOpenHashMap<>(); + + for (BlockPos pos : determineShapePositions()) { + final var randomSeed = random.nextLong(); // Fully deterministic regardless of chunk generation order + generatedBlocks.put(pos, (access, section) -> placeBlock(access, section, randomSeed, pos, entry)); + } + + return generatedBlocks; + } + + private void placeBlock(BulkSectionAccess level, LevelChunkSection section, long randomSeed, BlockPos pos, GTOreDefinition entry) { + RandomSource rand = new XoroshiroRandomSource(randomSeed); + + // ... + } + } + ``` \ No newline at end of file diff --git a/docs/content/Development/General-Topics/Overclocking-And-Parallel-Logic.md b/docs/content/Development/General-Topics/Overclocking-And-Parallel-Logic.md new file mode 100644 index 0000000000..bcffe523bc --- /dev/null +++ b/docs/content/Development/General-Topics/Overclocking-And-Parallel-Logic.md @@ -0,0 +1,80 @@ +--- +title: Overclocking & Parallel Logic +--- + + +# How to set up Overclocking and Parallel Logic + +To improve its generality, RecipeLogic has been rewritten in GTCEu Modern to support inputs and outputs other than +EU, items and fluids. + +The new `RecipeLogic` no longer handles overclocked and parallel logic, but instead delegates it to the +machine via `IRecipeLogicMachine`: + +```java +/** + * Override it to modify recipe on the fly e.g. applying overclock, + * change chance, etc + * + * @param recipe recipe from detected from GTRecipeType + * @return modified recipe. + * null -- this recipe is unavailable + */ +@Nullable +GTRecipe modifyRecipe(GTRecipe recipe); +``` + + +## Electric Overclocking + +In general, a simple electric overclocking can be done this way. For details, see `OverclockingLogic` and `RecipeHelper` + +```java +public @Nullable GTRecipe modifyRecipe(GTRecipe recipe) { + if (RecipeHelper.getRecipeEUtTier(recipe) > getTier()) { + return null; + } + + return RecipeHelper.applyOverclock( + getDefinition().getOverclockingLogic(), + recipe, + getMaxVoltage() + ); +} +``` + + +## Parallel Logic + +Parallel is also not complicated to implement. Let's take the `generator` as an example + +```java +public @Nullable GTRecipe modifyRecipe(GTRecipe recipe) { + var EUt = RecipeHelper.getOutputEUt(recipe); // get the recipe's EU/t + + if (EUt > 0) { + // calculate the max parallel limitation. + var maxParallel = (int) (Math.min( + energyContainer.getOutputVoltage(), + GTValues.V[overclockTier] + ) / EUt); + + while (maxParallel > 0) { + // copy and apply parallel, it will affect all recipes' contents + // and the recipe duration. + var copied = recipe.copy(ContentModifier.multiplier(maxParallel)); + + // If the machine has enough ingredients, return copied recipe. + if (copied.matchRecipe(this)) { + copied.duration = copied.duration / maxParallel; + + return copied; + } + + // Trying to halve the number of parallelism + maxParallel /= 2; + } + } + return null; +} +``` diff --git a/docs/content/Development/General-Topics/Tick-Updates.md b/docs/content/Development/General-Topics/Tick-Updates.md new file mode 100644 index 0000000000..328dc2c17a --- /dev/null +++ b/docs/content/Development/General-Topics/Tick-Updates.md @@ -0,0 +1,171 @@ +--- +title: Tick Updates +--- + + +# How to use `ITickable` / `update()` + +The client update is always present and you can override the `clientTick()` method, which works just as well as in 1.12. + +But for the sake of performance, our machines are no longer always in a tickable state. +We introduced `ITickSubscription` for managed tick logic. +Understand the basic concept of subscribing to periodic updates when they are needed, and unsubscribe them when +they are not. + + +## Example implementation + +Automatic output of our machine requires periodic output of internal items to an adjacent inventory. +But most of the time this logic doesn't need to be executed if any of the following conditions apply: + +- there is no item inside the machine +- the automatic output is not set to active +- there is no adjacent block that can accept the item. + +Lets look at how we implement it in `QuantumChest`. + + +??? example "Implementation in `QuantumChest`" + + ```java + @Getter @Persisted @DescSynced + protected boolean autoOutputItems; + @Persisted @DropSaved + protected final NotifiableItemStackHandler cache; // inner inventory + protected TickableSubscription autoOutputSubs; + protected ISubscription exportItemSubs; + + // update subscription, subscribe if tick logic subscription is required, unsubscribe otherwise. + protected void updateAutoOutputSubscription() { + var outputFacing = getOutputFacingItems(); // get output facing + if ((isAutoOutputItems() && !cache.isEmpty()) // inner item non empty + && outputFacing != null // has output facing + && ItemTransferHelper.getItemTransfer(getLevel(), getPos().relative(outputFacing), outputFacing.getOpposite()) != null) { // adjacent block has inventory. + autoOutputSubs = subscribeServerTick(autoOutputSubs, this::checkAutoOutput); // subscribe tick logic + } else if (autoOutputSubs != null) { // unsubscribe tick logic + autoOutputSubs.unsubscribe(); + autoOutputSubs = null; + } + } + + // output to nearby block. + protected void checkAutoOutput() { + if (getOffsetTimer() % 5 == 0) { + if (isAutoOutputItems() && getOutputFacingItems() != null) { + cache.exportToNearby(getOutputFacingItems()); + } + updateAutoOutputSubscription(); // dont foget to check if it's still available + } + } + + @Override + public void onLoad() { + super.onLoad(); + if (getLevel() instanceof ServerLevel serverLevel) { + // you cant call ItemTransferHelper.getItemTransfer while chunk is loading, so lets defer it next tick. + serverLevel.getServer().tell(new TickTask(0, this::updateAutoOutputSubscription)); + } + // add a listener to listen the changes of inner inventory. (for ex, if inventory not empty anymore, we may need to unpdate logic) + exportItemSubs = cache.addChangedListener(this::updateAutoOutputSubscription); + } + + @Override + public void onUnload() { + super.onUnload(); //autoOutputSubs will be released automatically when machine unload + if (exportItemSubs != null) { //we should mannually release it. + exportItemSubs.unsubscribe(); + exportItemSubs = null; + } + } + + // For any change may affect the logic to invoke updateAutoOutputSubscription at a time + @Override + public void setAutoOutputItems(boolean allow) { + this.autoOutputItems = allow; + updateAutoOutputSubscription(); + } + + @Override + public void setOutputFacingItems(Direction outputFacing) { + this.outputFacingItems = outputFacing; + updateAutoOutputSubscription(); + } + + @Override + public void onNeighborChanged(Block block, BlockPos fromPos, boolean isMoving) { + super.onNeighborChanged(block, fromPos, isMoving); + updateAutoOutputSubscription(); + } + ``` + +I know the code is a kinda long, but it's for performance, and thanks to the SyncData system, we've eliminated a lot of +synchronization code, so please sacrifice a little for better performance. + + +## Using the `ConditionalSubscriptionHandler` + +For ease of use in some situations, it is possible to eliminate some of the boilerplate code and delegate management of +your subscription to a `ConditionalSubscriptionHandler` instead. + +Using that class, it is possible to simply provide an update method to run every tick while the subscription is active, +as well as a `Supplier` that determines whether it is active. + +Whenever the input of its condition changes, you need to call `updateSubscription()` on the handler, so that it can +re-evaluate it and take the necessary steps if it has changed. +You should also to call this method after executing your tick logic in most cases, to ensure the subscription doesn't +stay active any longer than it needs to. + +??? example "Example using `ConditionalSubscriptionHandler`" + + ```java + class MyMachine extends MetaMachine implements IControllable { + @Persisted @Getter + private boolean workingEnabled = true; + + private final ConditionalSubscriptionHandler subscriptionHandler; + + public MyMachine() { + super(/* ... */); + + this.subscriptionHandler = new ConditionalSubscriptionHandler( + this, this::update, this::isSubscriptionActive + ); + } + + private void update() { + // Only run once every second + if (getOffsetTimer() % 20 != 0) + return; + + // ... + + // Now that the update logic has been executed, update the subscription. + // This will internally check if the subscription is still active and + // unsubscribe otherwise. + subscriptionHandler.updateSubscription(); + } + + private boolean isSubscriptionActive() { + return isWorkingEnabled(); + } + + @Override + public void setWorkingEnabled(boolean workingEnabled) { + this.workingEnabled = workingEnabled; + + // Whether the subscription is currently active depends on whether working + // is enabled for this machine. As soon as any of the condition inputs changes, + // you need to update the subscription. + subscriptionHandler.updateSubscription(); + } + + @Override + public void onLoad() { + super.onLoad(); + + // As soon as you can get a reference to the dimension/level you're in, + // you need to initialize your subscription handler. + subscriptionHandler.initialize(getLevel()); + } + } + ``` \ No newline at end of file diff --git a/docs/content/Development/Glossary.md b/docs/content/Development/Glossary.md new file mode 100644 index 0000000000..d0c86459ff --- /dev/null +++ b/docs/content/Development/Glossary.md @@ -0,0 +1,62 @@ +--- +icon: "material/information-box" +title: "Development Glossary" +search: + boost: 100 +--- + + + + + +# :material-information-box: Development Glossary + +!!! info + This is an overview of technical terms that are commonly used in this documentation. + If you're not sure what something means (or how it applies to the current context), please refer to this page. + + +## Client Side + +The part of the game running on a player's computer. + +It always hosts the [Remote Side](#remote-side). +In singleplayer mode, the [Server Side](#server-side) is hosted on the client as well. In multiplayer mode, the client +connects to a dedicated server instead. + + +## Remote Side + +!!! info inline end "See also: [Server Side](#server-side)" + +The remote side is the part of the game that is **connected to** the game's server side. +It always runs on the [client](#client-side). + +This side may not have the same amount of data available to it as the server does (see [SyncData](../Development/SyncData/index.md) if you +need to automatically synchronize certain data to the remote side). +It also does not perform any tick update logic. + + +## Server Side + +!!! info inline end "See also: [Remote Side](#remote-side)" + +The server side is what one or more players connect to. +In singleplayer mode, it runs on the [client](#client-side). In multiplayer mode, it runs on a dedicated server. + +This side usually has all of the world's data available to it and runs tick update logic. +This is therefore, where [TPS](#tps) impact becomes relevant. In general, use + + +## TPS + +Short for "ticks per second". Should stay at exactly 20. + +See [Tick Updates](General-Topics/Tick-Updates.md) and [Optimization](General-Topics/Optimization.md) for techniques +on how to reduce performance impact. \ No newline at end of file diff --git a/docs/content/Development/SyncData/.pages b/docs/content/Development/SyncData/.pages new file mode 100644 index 0000000000..772812dd31 --- /dev/null +++ b/docs/content/Development/SyncData/.pages @@ -0,0 +1,5 @@ +nav: + - index.md + - Using-SyncData.md + - Annotations + - ... diff --git a/docs/content/Development/SyncData/Annotations/.pages b/docs/content/Development/SyncData/Annotations/.pages new file mode 100644 index 0000000000..a094df0832 --- /dev/null +++ b/docs/content/Development/SyncData/Annotations/.pages @@ -0,0 +1,6 @@ +nav: + - Persisted.md + - DescSynced.md + - RequireRerender.md + - UpdateListener.md + - ... \ No newline at end of file diff --git a/docs/content/Development/SyncData/Annotations/DescSynced.md b/docs/content/Development/SyncData/Annotations/DescSynced.md new file mode 100644 index 0000000000..04848872b1 --- /dev/null +++ b/docs/content/Development/SyncData/Annotations/DescSynced.md @@ -0,0 +1,6 @@ +--- +title: "@DescSynced" +--- + + +# Using `@DescSynced` \ No newline at end of file diff --git a/docs/content/Development/SyncData/Annotations/Persisted.md b/docs/content/Development/SyncData/Annotations/Persisted.md new file mode 100644 index 0000000000..d6bdaf78d4 --- /dev/null +++ b/docs/content/Development/SyncData/Annotations/Persisted.md @@ -0,0 +1,6 @@ +--- +title: "@Persisted" +--- + + +# Using `@Persisted` \ No newline at end of file diff --git a/docs/content/Development/SyncData/Annotations/RequireRerender.md b/docs/content/Development/SyncData/Annotations/RequireRerender.md new file mode 100644 index 0000000000..f32382f036 --- /dev/null +++ b/docs/content/Development/SyncData/Annotations/RequireRerender.md @@ -0,0 +1,6 @@ +--- +title: "@RequireRerender" +--- + + +# Using `@RequireRerender` \ No newline at end of file diff --git a/docs/content/Development/SyncData/Annotations/UpdateListener.md b/docs/content/Development/SyncData/Annotations/UpdateListener.md new file mode 100644 index 0000000000..fbf84080cb --- /dev/null +++ b/docs/content/Development/SyncData/Annotations/UpdateListener.md @@ -0,0 +1,6 @@ +--- +title: "@UpdateListener" +--- + + +# Using `@UpdateListener` \ No newline at end of file diff --git a/docs/content/Development/SyncData/Using-SyncData.md b/docs/content/Development/SyncData/Using-SyncData.md new file mode 100644 index 0000000000..5c981698f5 --- /dev/null +++ b/docs/content/Development/SyncData/Using-SyncData.md @@ -0,0 +1,50 @@ +--- +title: Using SyncData +--- + + +# How To Use SyncData Annotations + +For serializing and synchronizing fields, LDLib's annotation-based SyncData system is used. + +Please also refer to the [LDLib Wiki](https://github.com/Low-Drag-MC/LDLib-Architectury/wiki/SyncData-Annotations). + + +## Overview + +Here is an overview of the most important annotations: + +```java +public class MyMachine { + @Persisted // (1) + private String myPersistedField; + + @DescSynced // (2) + private String myClientsideRelevantField; + + @DescSynced @RequireRerender // (3) + private IO io; + + @DescSynced @UpdateListener(methodName = "runAdditionalUpdate") // (4) + private int fieldWithAdditionalClientUpdateLogic; + + + private void runAdditionalUpdate(int newValue, int oldValue) { + // Run additional clientside update code here + } +} +``` + +1. This field is automatically serialized to and deserialized from NBT data, that will be stored with its container. + By default, `@Persisted` only applies on the server side. + +2. For fields that need to be available on the client (or more specifically, remote) side, you can annotate them with + `@DescSynced` to make them available there as well. + Any changes made to the field on the server side will automatically be synchronized to the client side. + +3. If a change of a synced field's value requires rerendering the containing block (e.g. for different overlays based + on a cover's IO direction), simply add the `@RequireRerender` annotation to it. + Its renderer's code will then be called again every time the field changes. + +4. In some cases, a field may require some additional code to run on the client/remote side when it has been synced. + The `@UpdateListener` annotation allows you to define a method to be called in that case. \ No newline at end of file diff --git a/docs/content/Development/SyncData/index.md b/docs/content/Development/SyncData/index.md new file mode 100644 index 0000000000..4abfcf8afb --- /dev/null +++ b/docs/content/Development/SyncData/index.md @@ -0,0 +1,12 @@ +--- +title: "LDLib's SyncData System" +--- + + +# LDLib's SyncData System + +For serializing and synchronizing fields, LDLib's annotation-based SyncData system is used. +Using this system, almost all boilerplate code regarding these topics can be omitted. + +For more info on the SyncData annotations, please refer to the following chapters, as well as to the +[LDLib Wiki](https://github.com/Low-Drag-MC/LDLib-Architectury/wiki/SyncData-Annotations). \ No newline at end of file diff --git a/docs/content/Development/index.md b/docs/content/Development/index.md new file mode 100644 index 0000000000..796b0d5f65 --- /dev/null +++ b/docs/content/Development/index.md @@ -0,0 +1,22 @@ +--- +title: Developer Documentation +--- + + +# Developing GTCEu Modern + +If you want to contribute to the development of GTCEu Modern, please feel free to submit a +pull request with your changes. + +The following pages describe a few important concepts that you will likely run into when working with our codebase. + +!!! link "LDLib Docs" + [:material-github: LDLib-Architectury :material-arrow-right: Wiki](https://github.com/Low-Drag-MC/LDLib-Architectury/wiki) + + This mod is based on the LDLib library for a lot of comminly used functionalities. + Please refer to its documentation as well. + +!!! link "Architectury Gradle Plugin" + [Architectury-Wiki :material-arrow-right: Gradle Plugin](https://docs.architectury.dev/plugin/introduction) + + This mod is using Architectury's build system, for compatibiliy across multiple moding platforms. \ No newline at end of file diff --git a/docs/content/Gameplay/Ore-Generation.md b/docs/content/Gameplay/Ore-Generation.md new file mode 100644 index 0000000000..0b872dd46f --- /dev/null +++ b/docs/content/Gameplay/Ore-Generation.md @@ -0,0 +1,380 @@ +--- +title: "Ore Generation" +--- + + +# Ore Generation + +This is an overview of GTCEu's ore veins and the ore types they contain. +Please note that these are the default settings and may be different in certain modpacks. + + +## Overworld + + +### Apatite Vein + +- Apatite +- Tricalcium Phosphate +- Pyrochlore + +**Height range:** 10 to 80 + + +### Cassiterite Vein + +- Tin +- Cassiterite + +**Height range:** 10 to 80 + + +### Coal Vein + +- Coal + +**Height range:** 10 to 140 + + +### Copper Tin Vein + +- Chalcopyrite +- Zeolite +- Cassiterite +- Realgar + +**Height range:** -10 to 160 + + +### Galena Vein + +- Galena +- Silver +- Lead + +**Height range:** -15 to 45 + + +### Garnet Tin Vein + +- Cassiterite Sand +- Garnet Sand +- Asbestos +- Diatomite + +**Height range:** 30 to 60 + + +### Iron Vein + +- Goethite +- Yellow Limonite +- Hematite +- Malachite + +**Height range:** -10 to 60 + + +### Lubricant Vein + +- Soapstone +- Talc +- Glauconite Sand +- Pentlandite + +**Height range:** 0 to 50 + + +### Magnetite Vein + +- Magnetite +- Vanadium Magnetite +- Gold + +**Height range:** 10 to 60 + + +### Mineral Sand Vein + +- Basaltic Mineral Sand +- Granitic Mineral Sand +- Fuller's Earth +- Gypsum + +**Height range:** 15 to 60 + + +### Nickel Vein + +- Garnierite +- Nickel +- Cobaltite +- Pentlandite + +**Height range:** -10 to 60 + + +### Salts Vein + +- Rock Salt +- Salt +- Lepidolite +- Spodumene + +**Height range:** 30 to 70 + + +### Oilsands Vein + +- Oilsands + +**Height range:** 30 to 80 + + + +### Copper Vein + +- Chalcopyrite +- Iron +- Pyrite +- Copper + +**Height range:** -40 to 10 + + +### Diamond Vein + +- Graphite +- Diamond +- Coal + +**Height range:** -65 to -30 + + +### Lapis Vein + +- Lazurite +- Sodalite +- Lapis +- Calcite + +**Height range:** -60 to 10 + + +### Manganese Vein + +- Grossular +- Spessartine +- Pyrolusite +- Tantalite + +**Height range:** -30 to 0 + + +### Mica Vein + +- Kyanite +- Mica +- Bauxite +- Pollucite + +**Height range:** -40 to -10 + + +### Olivine Vein + +- Bentonite +- Magnetite +- Olivine +- Glauconite Sand + +**Height range:** -20 to 10 + + +### Redstone Vein + +- Redstone +- Ruby +- Cinnabar + +**Height range:** -65 to -10 + + +### Sapphire Vein + +- Almandine +- Pyrope +- Sapphire +- Green Sapphire + +**Height range:** -40 to 0 + + +## Nether + + +### Banded Iron Vein + +- Goethite +- Yellow Limonite +- Hematite +- Gold + +**Height range:** 20 to 40 + + +### Beryllium Vein + +- Beryllium +- Emerald + +**Height range:** 5 to 30 + + +### Certus Quartz Vein + +- Quartzite +- Certus Quartz +- Barite + +**Height range:** 80 to 120 + + +### Manganese Vein (Nether) + +- Grossular +- Pyrolusite +- Tantalite + +**Height range:** 20 to 30 + + +### Molybdenum Vein + +- Wulfenite +- Molybdenite +- Molybdenum +- Powellite + +**Height range:** 20 to 50 + + +### Monazite Vein + +- Bastnasite +- Molybdenum +- Neodymium + +**Height range:** 20 to 40 + + +### Nether Quartz Vein + +- Nether Quartz +- Quartzite + +**Height range:** 40 to 80 + + +### Redstone Vein (Nether) + +- Redstone +- Ruby +- Cinnabar + +**Height range:** 5 to 40 + + +### Saltpeter Vein + +- Saltpeter +- Diatomite +- Electrotine +- Alunite + +**Height range:** 5 to 45 + + +### Sulfur Vein + +- Sulfur +- Pyrite +- Sphalerite + +**Height range:** 10 to 30 + + +### Tetrahedryte Vein + +- Tetrahedryte +- Copper +- Stibnite + +**Height range:** 80 to 120 + + +### Topaz Vein + +- Blue Topaz +- Topaz +- Chalcocite +- Bornite + +**Height range:** 80 to 120 + + +## The End + + +### Bauxite Vein + +- Bauxite +- Ilmenite +- Aluminium + +**Height range:** 10 to 80 + + +### Magnetite Vein (The End) + +- Magnetite +- Vanadium Magnetite +- Chromite +- Gold + +**Height range:** 20 to 80 + + +### Naquadah Vein + +- Naquadah +- Plutonium 239 + +**Height range:** 10 to 90 + + +### Pitchblende Vein + +- Pitchblende +- Uraninite + +**Height range:** 30 to 60 + + +### Scheelite Vein + +- Scheelite +- Tungstate +- Lithium + +**Height range:** 20 to 60 + + +### Sheldonite Vein + +- Bornite +- Cooperite +- Platinum +- Palladium + +**Height range:** 5 to 50 diff --git a/docs/content/Gameplay/Steam/Bronze.md b/docs/content/Gameplay/Steam/Bronze.md new file mode 100644 index 0000000000..f3f9a381d9 --- /dev/null +++ b/docs/content/Gameplay/Steam/Bronze.md @@ -0,0 +1,13 @@ +--- +title: Bronze +--- + +# Bronze + +Bronze is the most important material in the [Steam Age](./index.md). + +To prepare the first batch of it you will need [Tin and Copper](../Ore-Generation.md) just crush the ingots with a GT mortar. And then mix them in a crafting table in _3_ to _1_ proportion. Then smelting to receive the ingot. + +![Ingot crushing recipe](./assets/ingot_crushed.png) +![Bronze dust recipe](./assets/bronze_recipe.png) + diff --git a/docs/content/Gameplay/Steam/Steam-Generation.md b/docs/content/Gameplay/Steam/Steam-Generation.md new file mode 100644 index 0000000000..eadb41b291 --- /dev/null +++ b/docs/content/Gameplay/Steam/Steam-Generation.md @@ -0,0 +1,49 @@ +--- +Title: Generating Steam +--- + +# Generating Steam + +## Early game + +For early game steam generation you have two options: + +### Boilers + * Liquid - allowing you to use hot liquids like lava. To heat up water and get steam. + * Solid - uses coal to heat up the water. +### Solar Boilers + * generate steam from solar boilers _doesn't work at night_. + +For water you can use a **primitive pump** +![Primitive water pump](./assets/p_water_pump.png) + +or use a water connection from any other mod. + +## After Steel + +After unlocking steel (using a primitive blast furnace) you will get access to the high pressure versions of the boilers and allow you to produce more steam. + +## Large Boilers + +Large boilers allow you to create massive amount of steam in the blink of an eye. A boiler can be constructed of many different GT materials, that will allow for different steam generation rates (_consult the table below_). + +## Base Generation Rates + +### Single Block + +| Boiler type | Low pressure | High pressure | +| ----------- | ------------ | ------------- | +| Solar | 6 L/T | 18 L/T | +| Liquid | 12 L/T | 30 L/T | +| Solid | 6 L/T | 15 L/T | + +### Multiblock + +| Boiler | Generation | Boil up | Max temp | +| ------------- | ---------- | ------- | -------- | +| Bronze | 800 L/T | 40s | 1074K | +| Steel | 1800 L/T | 90s | 2074K | +| Titanium | 3200 L/T | 160s | 3474K | +| Tungstensteel | 6400 L/T | 160s | 6674K | + +*L/T -> Liters per tick (1L = 1mB) \ No newline at end of file diff --git a/docs/content/Gameplay/Steam/assets/bronze_recipe.png b/docs/content/Gameplay/Steam/assets/bronze_recipe.png new file mode 100644 index 0000000000000000000000000000000000000000..96592c00b31bb3a98d6855add91e132c0355236e GIT binary patch literal 1571 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 z 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 t` to `#gtceu:circuits/`. +You will need to update any recipes accordingly. + + +## Recipe ID Changes + +Several recipe IDs have changed. If you are removing/modifying any recipes by ID, please check if they are still valid. + + +## Ore Stone Types + +Due to changes in how ores are rendered, ore stone types will now additionally require a base model location: + +```js +e.create('my_ore', 'ore') + .baseModelLocation('some_other_mod:block/my_block') + //... +``` \ No newline at end of file diff --git a/docs/content/Modpacks/Changes/v1.2.1.md b/docs/content/Modpacks/Changes/v1.2.1.md new file mode 100644 index 0000000000..5fcfe39914 --- /dev/null +++ b/docs/content/Modpacks/Changes/v1.2.1.md @@ -0,0 +1,58 @@ +--- +title: "Version 1.2.1" +--- + + +# Updating from `1.2.0` to `1.2.1` + + +## Custom Coils + +The `coilMaterial` function now uses a supplier instead of taking a material directly. + +```js +// Before: +.coilMaterial(GTMaterials.get('infinity')) + +// After: +.coilMaterial(() => GTMaterials.get('infinity')) +``` + + +## Recipe Modifiers + +If any of your machines had a custom recipe modifier, its syntax has changed slightly. + +More than one recipe modifier can now be applied, making more complex chains of modifiers easier to declare. +In particular, multiblocks supporting parallel hatches now need to be declared differently: + +```js +// Before: +.recipeModifier(GTRecipeModifiers.PARALLEL_HATCH.apply(OverclockingLogic.PERFECT_OVERCLOCK, GTRecipeModifiers.ELECTRIC_OVERCLOCK)) + +// After: +.recipeModifiers([GTRecipeModifiers.PARALLEL_HATCH, GTRecipeModifiers.ELECTRIC_OVERCLOCK.apply(OverclockingLogic.PERFECT_OVERCLOCK)]) +``` +Recipe modifiers must be supplied as a JavaScript array, as this is what Rhino transpiles into a varargs (```RecipeModifier...```) value, which is what the ```.recipeModifiers()``` method accepts as input. If not supplied as an array, Rhino with throw an exception and crash Minecraft. + +## Bedrock Ores + +Bedrock ore veins are no longer automatically generated. +They are now entirely up to modpack developers to define, and offer more flexibility than the previous system. + + +```js +GTCEuServerEvents.oreVeins(event => { + event.add('kubejs:my_custom_bedrock_vein', vein => { + // ... + }) + event.modify('kubejs:other_custom_vein', vein => { + // ... + }) + event.remove('kubejs:other_custom_vein') +}) +``` + +The documentation for how to use the add and modify events will follow soon. +For now, please reference the [`BedrockOreDefinition.Builder`](https://github.com/GregTechCEu/GregTech-Modern/blob/1.20.1/src/main/java/com/gregtechceu/gtceu/api/data/worldgen/bedrockore/BedrockOreDefinition.java#L117) class in our source code. + diff --git a/docs/content/Modpacks/Examples/Alternator.md b/docs/content/Modpacks/Examples/Alternator.md new file mode 100644 index 0000000000..2b2ce01e09 --- /dev/null +++ b/docs/content/Modpacks/Examples/Alternator.md @@ -0,0 +1,72 @@ +--- +title: "Alternator" +--- + + +## Alternator Multiblock (by Drack.ion) + +### Recipe Type + +```js title="alternator_recipe_type.js" +GTCEuStartupEvents.registry('gtceu:recipe_type', event => { + event.create('basic_alternator') + .category('multiblock') + .setEUIO('out') + .setMaxIOSize(1, 0, 0, 0) + .setProgressBar(GuiTextures.PROGRESS_BAR_ARROW, FillDirection.LEFT_TO_RIGHT) + .setSound(GTSoundEntries.ARC) + .setMaxTooltips(6) +}) +``` + +### Multiblock + +```js title="alternator_multiblock.js" +GTCEuStartupEvents.registry('gtceu:machine', event => { + event.create('basic_alternator', 'multiblock') + .rotationState(RotationState.NON_Y_AXIS) + .recipeType('basic_alternator') + .appearanceBlock(GTBlocks.CASING_STEEL_SOLID) + .generator(true) + .pattern(definition => FactoryBlockPattern.start() + .aisle("CWC", "CWC", "#W#") + .aisle("CWC", "K#E", "CWC") + .aisle("CWC", "CWA", "#W#") + .where('A', Predicates.controller(Predicates.blocks(definition.get()))) + .where('W', Predicates.blocks(GTBlocks.COIL_CUPRONICKEL.get())) + .where("C", Predicates.blocks(GTBlocks.CASING_STEEL_SOLID.get())) + .where('#', Predicates.any()) + .where('K', Predicates.abilities(PartAbility.INPUT_KINETIC).setExactLimit(1)) + .where('E', Predicates.abilities(PartAbility.OUTPUT_ENERGY).setExactLimit(1)) + .build() + ) + .workableCasingRenderer( + "gtceu:block/casings/solid/machine_casing_solid_steel", + "gtceu:block/multiblock/implosion_compressor", false + ) +}) +``` + +### Lang + +```json title="en_us.json" +{ + "block.gtceu.basic_alternator": "Basic Alternator", + "gtceu.basic_alternator": "Basic Alternator" +} +``` + +### Recipes + +```js title="alternator_recipes.js" +ServerEvents.recipes(event => { + function basic_alt(id, rpm, eu){ + event.recipes.gtceu.basic_alternator(id) + .circuit(1) + .rpm(rpm) + .duration(2) + .EUt(eu) + } + basic_alt('lv_1_amp', 32, -32) +}) +``` diff --git a/docs/content/Modpacks/Examples/Greenhouse.md b/docs/content/Modpacks/Examples/Greenhouse.md new file mode 100644 index 0000000000..7c910abcb1 --- /dev/null +++ b/docs/content/Modpacks/Examples/Greenhouse.md @@ -0,0 +1,218 @@ +--- +title: "Greenhouse" +--- + + +# Greenhouse Multiblock (by Drack.ion) + + +## Recipe Type + +```js title="greenhouse_recipe_type.js" +GTCEuStartupEvents.registry('gtceu:recipe_type', event => { + event.create('greenhouse') + .category('drack') + .setEUIO('in') + .setMaxIOSize(3, 4, 1, 0) + .setProgressBar(GuiTextures.PROGRESS_BAR_ARROW, FillDirection.LEFT_TO_RIGHT) + .setSound(GTSoundEntries.BATH) +}) +``` + + +## Multiblock + +```js title="greenhouse_multiblock.js" +GTCEuStartupEvents.registry('gtceu:machine', event => { + event.create('greenhouse', 'multiblock') + .rotationState(RotationState.NON_Y_AXIS) + .recipeType('greenhouse') + .appearanceBlock(GTBlocks.CASING_STEEL_SOLID) + .pattern(definition => FactoryBlockPattern.start() + .aisle('CCC', 'CGC', 'CGC', 'CLC', 'CCC') + .aisle('CMC', 'GSG', 'G#G', 'LIL', 'COC') + .aisle('CKC', 'CGC', 'CGC', 'CLC', 'CNC') + .where('K', Predicates.controller(Predicates.blocks(definition.get()))) + .where('M', Predicates.blocks('moss_block') + .or(Predicates.blocks('dirt')) + .or(Predicates.blocks('grass_block')) + ) + .where('G', Predicates.blocks('ae2:quartz_glass')) + .where('S', Predicates.blocks('oak_sapling') + .or(Predicates.blocks('dark_oak_sapling')) + .or(Predicates.blocks('spruce_sapling')) + .or(Predicates.blocks('birch_sapling')) + .or(Predicates.blocks('jungle_sapling')) + .or(Predicates.blocks('acacia_sapling')) + .or(Predicates.blocks('azalea')) + .or(Predicates.blocks('flowering_azalea')) + .or(Predicates.blocks('mangrove_propagule')) + .or(Predicates.blocks('gtceu:rubber_sapling')) + ) + .where('I', Predicates.blocks('glowstone')) + .where('L', Predicates.blocks(GTBlocks.CASING_GRATE.get())) + .where('C', Predicates.blocks(GTBlocks.CASING_STEEL_SOLID.get()) + .or(Predicates.autoAbilities(definition.getRecipeTypes())) + ) + .where('O', Predicates.abilities(PartAbility.MUFFLER) + .setExactLimit(1) + ) + .where('N', Predicates.abilities(PartAbility.MAINTENANCE)) + .where('#', Predicates.air()) + .build() + ) + .workableCasingRenderer('gtceu:block/casings/solid/machine_casing_solid_steel', 'gtceu:block/multiblock/implosion_compressor', false) +}) +``` + + +## Lang + +```json title="en_us.json" +{ + "block.gtceu.greenhouse": "Greenhouse", + "gtceu.greenhouse": "Greenhouse" +} +``` + + +## Recipes + +```js title="greenhouse_recipes.js" +ServerEvents.recipes(event => { + + ////// Machine Recipe ////// + + event.shaped( + 'gtceu:greenhouse', + ['AWA', 'CSC', 'WCW'], + { + A: '#forge:circuits/mv', + W: 'gtceu:copper_single_cable', + C: '#forge:circuits/mv', + S: 'gtceu:solid_machine_casing' + } + ).id('gtceu:shaped/greenhouse') + + + ////// Greenhouse Recipes ////// + + function Greenhouse(id, input, fluid, output, boosted) { + if (boosted) { + event.recipes.gtceu.greenhouse(id) + .circuit(2) + .notConsumable(InputItem.of(input)) + .itemInputs('4x gtceu:fertilizer') + .inputFluids(Fluid.of('minecraft:water', fluid)) + .itemOutputs(output) + .duration(320) + .EUt(MV) + } else { + event.recipes.gtceu.greenhouse(id) + .circuit(1) + .notConsumable(InputItem.of(input)) + .inputFluids(Fluid.of('minecraft:water', fluid)) + .itemOutputs(output) + .duration(640) + .EUt(MV) + } + } + + + ////// Trees ////// + + // Rubber + Greenhouse('rubber_sapling', 'gtceu:rubber_sapling', 1000, ['32x gtceu:rubber_log', '8x gtceu:sticky_resin', '4x gtceu:rubber_sapling'], false) + Greenhouse('rubber_sapling_boosted', 'gtceu:rubber_sapling', 1000, ['64x gtceu:rubber_log', '16x gtceu:sticky_resin', '4x gtceu:rubber_sapling'], true) + + // Oak + Greenhouse('oak_sapling', 'minecraft:oak_sapling', 1000, ['64x minecraft:oak_log', '4x minecraft:oak_sapling'], false) + Greenhouse('oak_sapling_boosted', 'minecraft:oak_sapling', 1000, ['64x minecraft:oak_log', '64x minecraft:oak_log', '4x minecraft:oak_sapling'], true) + + // Dark Oak + Greenhouse('dark_oak_sapling', 'minecraft:dark_oak_sapling', 1000, ['64x minecraft:dark_oak_log', '4x minecraft:dark_oak_sapling'], false) + Greenhouse('dark_oak_sapling_boosted', 'minecraft:dark_oak_sapling', 1000, ['64x minecraft:dark_oak_log', '64x minecraft:dark_oak_log', '4x minecraft:dark_oak_sapling'], true) + + // Spruce + Greenhouse('spruce_sapling', 'minecraft:spruce_sapling', 1000, ['64x minecraft:spruce_log', '4x minecraft:spruce_sapling'], false) + Greenhouse('spruce_sapling_boosted', 'minecraft:spruce_sapling', 1000, ['64x minecraft:spruce_log', '64x minecraft:spruce_log', '4x minecraft:spruce_sapling'], true) + + // Birch + Greenhouse('birch_sapling', 'minecraft:birch_sapling', 1000, ['64x minecraft:birch_log', '4x minecraft:birch_sapling'], false) + Greenhouse('birch_sapling_boosted', 'minecraft:birch_sapling', 1000, ['64x minecraft:birch_log', '64x minecraft:birch_log', '4x minecraft:birch_sapling'], true) + + // Acacia + Greenhouse('acacia_sapling', 'minecraft:acacia_sapling', 1000, ['64x minecraft:acacia_log', '4x minecraft:acacia_sapling'], false) + Greenhouse('acacia_sapling_boosted', 'minecraft:acacia_sapling', 1000, ['64x minecraft:acacia_log', '64x minecraft:acacia_log', '4x minecraft:acacia_sapling'], true) + + // Jungle + Greenhouse('jungle_sapling', 'minecraft:jungle_sapling', 1000, ['64x minecraft:jungle_log', '4x minecraft:jungle_sapling'], false) + Greenhouse('jungle_sapling_boosted', 'minecraft:jungle_sapling', 1000, ['64x minecraft:jungle_log', '64x minecraft:jungle_log', '4x minecraft:jungle_sapling'], true) + + // Azalea + Greenhouse('azalea_sapling', 'minecraft:azalea', 1000, ['64x minecraft:oak_log', '4x minecraft:azalea'], false) + Greenhouse('azalea_boosted', 'minecraft:azalea', 1000, ['64x minecraft:oak_log', '64x minecraft:oak_log', '4x minecraft:azalea'], true) + + // Flowering Azalea + Greenhouse('flowering_azalea', 'minecraft:flowering_azalea', 1000, ['64x minecraft:oak_log', '4x minecraft:flowering_azalea'], false) + Greenhouse('flowering_azalea_boosted', 'minecraft:flowering_azalea', 1000, ['64x minecraft:oak_log', '64x minecraft:oak_log', '4x minecraft:flowering_azalea'], true) + + // Mangrove + Greenhouse('mangrove_propagule', 'minecraft:mangrove_propagule', 1000, ['64x minecraft:mangrove_log', '4x minecraft:mangrove_propagule'], false) + Greenhouse('mangrove_propagule_boosted', 'minecraft:mangrove_propagule', 1000, ['64x minecraft:mangrove_log', '64x minecraft:mangrove_log', '4x minecraft:mangrove_propagule'], true) + + ////// Crops ////// + + // Sugarcane + Greenhouse('sugar_cane', 'minecraft:sugar_cane', 1000, '24x minecraft:sugar_cane', false) + Greenhouse('sugar_cane_boosted', 'minecraft:sugar_cane', 1000, '48x minecraft:sugar_cane', true) + + // Kelp + Greenhouse('kelp', 'minecraft:kelp', 2000, '24x minecraft:kelp', false) + Greenhouse('kelp_boosted', 'minecraft:kelp', 2000, '48x minecraft:kelp', true) + + // Bamboo + Greenhouse('bamboo', 'minecraft:bamboo', 1000, '24x minecraft:bamboo', false) + Greenhouse('bamboo_boosted', 'minecraft:bamboo', 1000, '48x minecraft:bamboo', true) + + // Cactus + Greenhouse('cactus', 'minecraft:cactus', 1000, '24x minecraft:cactus', false) + Greenhouse('cactus_boosted', 'minecraft:cactus', 1000, '48x minecraft:cactus', true) + + // Wheat + Greenhouse('wheat', 'minecraft:wheat_seeds', 1000, '24x minecraft:wheat', false) + Greenhouse('wheat_boosted', 'minecraft:wheat_seeds', 1000, '48x minecraft:wheat', true) + + // Carrot + Greenhouse('carrot', 'minecraft:carrot', 1000, '24x minecraft:carrot', false) + Greenhouse('carrot_boosted', 'minecraft:carrot', 1000, '48x minecraft:carrot', true) + + // Potato + Greenhouse('potato', 'minecraft:potato', 1000, '24x minecraft:potato', false) + Greenhouse('potato_boosted', 'minecraft:potato', 1000, '48x minecraft:potato', true) + + // Beetroot + Greenhouse('beetroot', 'minecraft:beetroot_seeds', 1000, '24x minecraft:beetroot', false) + Greenhouse('beetroot_boosted', 'minecraft:beetroot_seeds', 1000, '48x minecraft:beetroot', true) + + // Mellon + Greenhouse('melon', 'minecraft:melon_seeds', 1000, '12x minecraft:melon', false) + Greenhouse('melon_boosted', 'minecraft:melon_seeds', 1000, '24x minecraft:melon', true) + + // Pumpkin + Greenhouse('pumpkin', 'minecraft:pumpkin_seeds', 1000, '12x minecraft:pumpkin', false) + Greenhouse('pumpkin_boosted', 'minecraft:pumpkin_seeds', 1000, '24x minecraft:pumpkin', true) + + // Nether Wart + Greenhouse('nether_wart', 'minecraft:nether_wart', 1000, '12x minecraft:nether_wart', false) + Greenhouse('nether_wart_boosted', 'minecraft:nether_wart', 1000, '24x minecraft:nether_wart', true) + + // Red Mushroom + Greenhouse('red_mushroom', 'minecraft:red_mushroom', 1000, '12x minecraft:red_mushroom', false) + Greenhouse('red_mushroom_boosted', 'minecraft:red_mushroom', 1000, '24x minecraft:red_mushroom', true) + + // Brown Mushroom + Greenhouse('brown_mushroom', 'minecraft:brown_mushroom', 1000, '12x minecraft:brown_mushroom', false) + Greenhouse('brown_mushroom_boosted', 'minecraft:brown_mushroom', 1000, '24x minecraft:brown_mushroom', true) +}) +``` \ No newline at end of file diff --git a/docs/content/Modpacks/Examples/Ore-Processing-Plant.md b/docs/content/Modpacks/Examples/Ore-Processing-Plant.md new file mode 100644 index 0000000000..2e83c04385 --- /dev/null +++ b/docs/content/Modpacks/Examples/Ore-Processing-Plant.md @@ -0,0 +1,62 @@ +--- +title: "Ore Processing Plant" +--- + + +# Ore Processing Plant Multiblock (by trulyno) + + +## Recipe Type + +```js title="ore_processing_plant.js" +GTCEuStartupEvents.registry('gtceu:recipe_type', event => { + event.create('ore_processing_plant') + .category('ore_processing_plant') + .setEUIO('in') + .setMaxIOSize(1, 8, 2, 1) + .setSound(GTSoundEntries.BATH); +}); +``` + + +## Multiblock + +```js title="ore_processing_plant.js" +GTCEuStartupEvents.registry('gtceu:machine', event => { + event.create('ore_processing_plant', 'multiblock') + .rotationState(RotationState.NON_Y_AXIS) + .recipeType('ore_processing_plant') + .recipeModifiers(GTRecipeModifiers.PARALLEL_HATCH, GTRecipeModifiers.ELECTRIC_OVERCLOCK.apply(OverclockingLogic.PERFECT_OVERCLOCK)) + .appearanceBlock(GTBlocks.CASING_TUNGSTENSTEEL_ROBUST) + .pattern(definition => FactoryBlockPattern.start() + .aisle(' AAA ', ' FFF ', ' FFF ', ' F ', ' ', ' ', ' ') + .aisle('AFFFA', 'FG GF', 'F F', ' F F ', ' FFF ', ' F ', ' B ') + .aisle('AFFFA', 'F P F', 'F P F', 'F P F', ' FPF ', ' FMF ', ' B B ') + .aisle('AFFFA', 'FG GF', 'F F', ' F F ', ' FFF ', ' F ', ' B ') + .aisle(' AAA ', ' FCF ', ' FFF ', ' F ', ' ', ' ', ' ') + .where('C', Predicates.controller(Predicates.blocks(definition.get()))) + .where('F', Predicates.blocks(GTBlocks.CASING_TUNGSTENSTEEL_ROBUST.get()) + .or(Predicates.autoAbilities(definition.getRecipeTypes())) + .or(Predicates.abilities(PartAbility.MAINTENANCE).setExactLimit(1)) + .or(Predicates.abilities(PartAbility.PARALLEL_HATCH).setMaxGlobalLimited(1))) + .where('M', Predicates.abilities(PartAbility.MUFFLER)) + .where('P', Predicates.blocks(GTBlocks.CASING_TUNGSTENSTEEL_PIPE.get())) + .where('G', Predicates.blocks(GTBlocks.CASING_TUNGSTENSTEEL_GEARBOX.get())) + .where('A', Predicates.blocks(GTBlocks.FIREBOX_TUNGSTENSTEEL.get())) + .where('B', Predicates.blocks('gtceu:bronze_machine_casing')) + .where(' ', Predicates.any()) + .build()) + .workableCasingRenderer("gtceu:block/casings/solid/machine_casing_robust_tungstensteel", + "gtceu:block/multiblock/primitive_blast_furnace", false); + }) +``` + + +## Lang + +```json title="en_us.json" +{ + "block.gtceu.ore_processing_plant": "Ore Processing Plant", + "gtceu.ore_processing_plant": "Ore Processing" +} +``` \ No newline at end of file diff --git a/docs/content/Modpacks/Examples/Parallel-Hatch-Part.md b/docs/content/Modpacks/Examples/Parallel-Hatch-Part.md new file mode 100644 index 0000000000..c27987f666 --- /dev/null +++ b/docs/content/Modpacks/Examples/Parallel-Hatch-Part.md @@ -0,0 +1,36 @@ +--- +title: "Custom Parallel Hatch" +--- + + +# Custom Parallel Hatch Multi-Part (By Sparked) + +## Parallel Hatch + +```js title="extra_parallel_hatch.js" + +const $ParallelHatchPartMachine = Java.loadClass( + 'com.gregtechceu.gtceu.common.machine.multiblock.part.ParallelHatchPartMachine' +) // (1) + +GTCEuStartupEvents.registry('gtceu:machine', event => { // (2) + event.create( + "uhv_parallel_hatch", // (3) + "custom", + (holder, tier) => { + return new $ParallelHatchPartMachine(holder, tier); + }, + GTValues.UHV // (4) + ) + .abilities(PartAbility.PARALLEL_HATCH) // (5) + .workableTieredHullRenderer(GTCEu.id("block/machines/parallel_hatch_mk4")) // (6) +}) +``` + +1. Loading the java class that is required to build the parallel hatch multi part +2. Using the GT registry event to register the multi part, which is part of machine registry +3. The ID for the new parallel hatch +4. The tier used for the parallel hatch +5. Specifying the multipart to use parallel hatch ability +6. The texture to use for the multipart, this example just uses the t4 texture as a placeholder + You can look at gtm's assets to see the animations and textures to edit \ No newline at end of file diff --git a/docs/content/Modpacks/Examples/Reducing-Duration.md b/docs/content/Modpacks/Examples/Reducing-Duration.md new file mode 100644 index 0000000000..37eaf834aa --- /dev/null +++ b/docs/content/Modpacks/Examples/Reducing-Duration.md @@ -0,0 +1,27 @@ +--- +title: "Machines Duration Reduction" +--- + + +# Reducing Duration Of All Machine Recipes + +## Reducing Script + +```js title="Reduce_Duration.js" +ServerEvents.recipes(event => { + event.forEachRecipe({ mod: 'gtceu' }, recipe => { // (1) + try { // (2) + var newDuration = recipe.get("duration") // (3) + recipe.set("duration", newDuration/10) // (4) + } catch (err) { // (5) + console.log(recipe.id + " has no duration field, skipped.") + } + }) +}) +``` + +1. A function to run code for every recipe in gregtech. +2. Uses a try to avoid using recipes that don't have a duration, like crafting. +3. Gets a variable of the duration current duration to change. +4. Edits the recipes duration to a tenth of the old recipes duration. +5. Catches the error if the recipe has no duration and logs it. \ No newline at end of file diff --git a/docs/content/Modpacks/Examples/Removing-Ore-Piles.md b/docs/content/Modpacks/Examples/Removing-Ore-Piles.md new file mode 100644 index 0000000000..c1658ba861 --- /dev/null +++ b/docs/content/Modpacks/Examples/Removing-Ore-Piles.md @@ -0,0 +1,23 @@ +--- +title: "Removing Surface Ore Indicators" +--- + + +# Removing Surface Ore Indicators + +## Removal Script + +```js title="remove_piles.js" +GTCEuServerEvents.oreVeins(event => { + event.modifyAll((veinId, vein) => { + vein.surfaceIndicatorGenerator(indicator => indicator + .block(Block.getBlock("minecraft:air")) // (1) + .placement("above") + .density(0.4) // Unsure if this matters + .radius(5) // Unsure if this matters + ) + }) +}) +``` + +1. Replacing where a ore pile would be with an air block, essentially removing it. \ No newline at end of file diff --git a/docs/content/Modpacks/Examples/index.md b/docs/content/Modpacks/Examples/index.md new file mode 100644 index 0000000000..a44de0d986 --- /dev/null +++ b/docs/content/Modpacks/Examples/index.md @@ -0,0 +1,8 @@ +--- +title: "Examples" +--- + + +# Examples + +This sections contains several examples you can use and adapt for your own modpack. \ No newline at end of file diff --git a/docs/content/Modpacks/Materials-and-Elements/01-Material-Creation.md b/docs/content/Modpacks/Materials-and-Elements/01-Material-Creation.md new file mode 100644 index 0000000000..68fac55d98 --- /dev/null +++ b/docs/content/Modpacks/Materials-and-Elements/01-Material-Creation.md @@ -0,0 +1,130 @@ +--- +title: Material Creation +--- + + +Materials are in-game items or fluids. They can be dusts, ingots, gems, fluids and all their derivatives. +To make a new material, write an `event.create()` call in the registering function, like in the examples. +Write inside the parentheses the name of the material inside `''` or `""`. +(**NOTE**: to add a material that is present on the periodic table, but doesn't have any in-game items/fluids, look below for how to do it) + +You can change the properties of the material by adding any combination of the following calls: + +- `.ingot()` will make the material have both an ingot and dust form. +- `.dust()` will make the material have a dust form. Don't use this together with `.ingot()`. +- `.gem()` will make the material have both a gem form and a dust form. Don't use those together with `.dust()` or `.ingot()` +- `.liquid()` will make the material have a liquid (fluid) form with liquid properties. +- `.gas()` will make the material have a gas (fluid) form with gas properties. +- `.plasma()` will make the material have a plasma (fluid) form with plasma properties. +- `.polymer()` will make the material have a dust form with polymer properties. +- `.burnTime(int burnTime)` will turn the material into a furnace fuel. +- `.fluidBurnTime(int burnTime)` defines how long the fluid of the material will burn. +- `.components(component1, component2, ...)` describes the composition. The components are a list of elements of the following form: `'Kx material_name'`, where `K` is a positive integer. +- `.iconSet(set)` gives the material an icon set. +- `.color(int colorCode)` gives the material a color. The color must be provided as a hex value in the following form: `0xNNNNNN`, where `N` are digits. +- `.secondaryColor(int colorCode)` gives the material a secondary color. If this is not being called, the secondary value will default to white(0xffffff). +- `.flags(flag1, flag2, ...)` can be used to select certain properties of the material, like generating gears, or disabling decomposition. +- `.element(element)` -> similar to `.components()`, but is used when the material represents an element. +- `.rotorStats(speed, damage, durability)` -> this will create a turbine rotor from this material. +- `.blastTemp()` is meant to be paired together with `.ingot()`. Will generate a EBF recipe (and an ABS recipe) based on the parameters you give it: + 1. temperature -> dictates what coil tier it will require (check the coil tooltips for their max temperature). + If the temperature is below 1000, it will also generate a PBF recipe. + If temperature is above 1750, a hot ingot will be generated, this requiring a Vacuum Freezer. + 2. (optional) gas tier -> can be `null` for none, `'low'` for nitrogen, `'mid'` for helium, `'high'` for argon, `'higher'` for neon or `'highest'` for krypton. + 3. (optional) EU per tick -> the recipe voltage + 4. (optional) duration in ticks -> how long the recipe should take +- `.ore()` will create an ore from the material. + - Optionally you can add any of these sets of parameters: + 1. is emissive -> `true` for emissive textures + 2. ore multiplier and byproduct multiplier -> how many crushed ores will be given from one raw ore and how many byproducts dusts will be given throughout the ore processing + 3. ore multiplier, byproduct multiplier, is emissive +- `.washedIn()` +- `.separatedIn()` +- `.separatedInto()` +- `.oreSmeltInto()` +- `.polarizesInto()` +- `.arcSmeltInto()` +- `.maceratesInto()` +- `.ingotSmeltInto()` +- `.addOreByproducts()` +- `.cableProperties()` generates wires and cables(if material is not a superconductor). The following parameter sets can be given: + 1. Voltage, amperage, loss per block + 2. Voltage, amperage, loss per block, is superconductor -> for a super conductor, set loss as 0 and is super conductor as true + 3. Voltage, amperage, loss per block, is super conductor, critical temperature +- `.toolProperties()` +- `.fluidPipeProperties()` +- `.itemPipeProperties()` +- `.addDefaultEnchant()` + +!!! tip "Harvest Level & Burn Time" + For `.ingot()`, `.dust()` and `.gem()`, optionally you can put inside the parentheses any of these sets of parameters: + + 1. harvest level (e.g. `.ingot(2)` will make the material have the harvest level of iron tools) + 2. harvest level, burn time (e.g. `ingot(2, 2000)` will make the material have the harvest level of iron tools and will burn in furnaces as fuel for 2000 ticks or 100 seconds). + +!!! tip "Disabling Decomposition" + Depending on the composition, GT will autogenerate an electrolyzer or centrifuge recipe to decompose the material. You can block that by adding the disable decomposition flag. `DISABLE_DECOMPOSITION` + +!!! tip "Choosing EU/t" + GT has some builtin constants to ease choosing the required EU/t: + - `GTValues.V` for a full amp of power at the selected tier + - `GTValues.VA` for a full amp, adjusted for cable loss + - `GTValues.VH` for half an amp + - `GTValues.VHA` for half an amp, adjusted for cable loss + + These values are arrays containing the respective EU/t values for each tier. + For example, you can get a full amp of EV power, adjusted for cable loss like this: + + ```js + GTValues.VA[GTValues.EV] + ``` + +??? tip "Color Pickers" + To chose a color for your material, you can checkout https://www.w3schools.com/colors/colors_picker.asp + After you select a color with the above tool, copy the 6 digits that follow the # under the color preview. +## Creating an Ingot + +```js title="ingot.js" +GTCEuStartupEvents.registry('gtceu:material', event => { + event.create('andesite_alloy') + .ingot() + .components('1x andesite', '1x iron') + .color(0x839689).iconSet(GTMaterialIconSet.DULL) + .flags(GTMaterialFlags.GENERATE_PLATE, GTMaterialFlags.GENERATE_GEAR, GTMaterialFlags.GENERATE_SMALL_GEAR) +}) +``` + +## Creating a Gem + +```js title="gem.js" +GTCEuStartupEvents.registry('gtceu:material', event => { + event.create('purple_coal') + .gem(2, 4000) + .element(GTElements.C) + .ore(2, 3) + .color(0x7D2DDB).iconSet(GTMaterialIconSet.LIGNITE) +}) +``` + +## Creating a Dust + +```js title="dust.js" +GTCEuStartupEvents.registry('gtceu:material', event => { + event.create('mysterious_dust') + .dust() + .cableProperties(GTValues.V[GTValues.LV], 69, 0, true) // (1) +}) +``` + +1. Voltage, Amperage, EU loss, Is Superconductor. + +## Creating a Fluid + +```js title="fluid.js" +GTCEuStartupEvents.registry('gtceu:material', event => { + event.create('mysterious_ooze') + .fluid() + .color(0x500bbf) + .fluidTemp(69420) +}) +``` diff --git a/docs/content/Modpacks/Materials-and-Elements/02-Element-Creation.md b/docs/content/Modpacks/Materials-and-Elements/02-Element-Creation.md new file mode 100644 index 0000000000..ecb09536ad --- /dev/null +++ b/docs/content/Modpacks/Materials-and-Elements/02-Element-Creation.md @@ -0,0 +1,29 @@ +--- +title: Element Creation +--- + + +## Element Creation + +Elements are the base of GT materials. Registering an element WILL NOT add any items. +To make a new element(NOTE: you can add only elements that are NOT present on the periodic table), +write an `event.create()` call in the registry function like in the example below. +Inside the parentheses the following parameters are introduced: + +1. Element Name -> use '' or "" to write the element name. +2. Proton Count(use -1 if it is not an element that will get a material). +3. Neutron Count(use -1 if it is not an element that will get a material). +4. Half Life Seconds(decay stuff. Use -1 if you don't need to use decay). +5. Material to decay to(more decay stuff. Use null). +6. Atomic Symbol(what will be displayed as in chemical formulas) -> use '' or "" to write the atomic symbol. +7. Is isotope(ex. Uranium 235 and Uranium 238. Use false if you are not making an isotope) + +When a material will be created from this element, the above properties will affect the auto-generated recipes. + +```js +GTCEuStartupEvents.registry('gtceu:element', event => { + event.create('test_element', 27, 177, -1, null, 'test', false) // (1) +}) +``` + +1. Element Name, Protons, Neutrons, Half Life Seconds, Decay To, Atomic Symbol, Is Isotope diff --git a/docs/content/Modpacks/Materials-and-Elements/Material-Flags.md b/docs/content/Modpacks/Materials-and-Elements/Material-Flags.md new file mode 100644 index 0000000000..b64387b405 --- /dev/null +++ b/docs/content/Modpacks/Materials-and-Elements/Material-Flags.md @@ -0,0 +1,67 @@ +--- +title: Material Flags +--- + + +# Material Flags + +Using material flags, you can specify several properties of each material, which +can influence how the material behaves, as well as which items are generated for it. + +!!! example "Using material Flags" + ```js + GTCEuStartupEvents.registry('gtceu:material', event => { + event.create('my_material') + // ... + .flags(GTMaterialFlags.FLAMMABLE) + }) + ``` + + +## Generate Items + +- `FORCE_GENERATE_BLOCK` +- `GENERATE_BOLT_SCREW` +- `GENERATE_DENSE` +- `GENERATE_FINE_WIRE` +- `GENERATE_FOIL` +- `GENERATE_FRAME` +- `GENERATE_GEAR` +- `GENERATE_LENS` +- `GENERATE_LONG_ROD` +- `GENERATE_PLATE` +- `GENERATE_RING` +- `GENERATE_ROD` +- `GENERATE_ROTOR` +- `GENERATE_ROUND` +- `GENERATE_SMALL_GEAR` +- `GENERATE_SPRING` +- `GENERATE_SPRING_SMALL` + + +## Other Flags + +- `BLAST_FURNACE_CALCITE_DOUBLE` +- `BLAST_FURNACE_CALCITE_TRIPLE` +- `DISABLE_ALLOY_BLAST` +- `DISABLE_ALLOY_PROPERTY` +- `CRYSTALLIZABLE` +- `DECOMPOSITION_BY_CENTRIFUGING` +- `DECOMPOSITION_BY_ELECTROLYZING` +- `DISABLE_DECOMPOSITION` +- `EXCLUDE_BLOCK_CRAFTING_BY_HAND_RECIPES` +- `EXCLUDE_BLOCK_CRAFTING_RECIPES` +- `EXCLUDE_PLATE_COMPRESSOR_RECIPES` +- `EXPLOSIVE` +- `FLAMMABLE` +- `HIGH_SIFTER_OUTPUT` +- `IS_MAGNETIC` +- `MORTAR_GRINDABLE` +- `NO_SMASHING` +- `NO_SMELTING` +- `NO_UNIFICATION` +- `NO_WORKING` +- `SOLDER_MATERIAL` +- `SOLDER_MATERIAL_BAD` +- `SOLDER_MATERIAL_GOOD` +- `STICKY` \ No newline at end of file diff --git a/docs/content/Modpacks/Materials-and-Elements/Material-Icon-Sets.md b/docs/content/Modpacks/Materials-and-Elements/Material-Icon-Sets.md new file mode 100644 index 0000000000..28177d3125 --- /dev/null +++ b/docs/content/Modpacks/Materials-and-Elements/Material-Icon-Sets.md @@ -0,0 +1,50 @@ +--- +title: Icon Sets +--- + + +# Icon Sets + +The material system uses icon sets to determine the textures of generated blocks and items. + + +## Available Icon Sets + +The following icon sets are available by default: + +- `GTMaterialIconSet.BRIGHT` +- `GTMaterialIconSet.CERTUS` +- `GTMaterialIconSet.DIAMOND` +- `GTMaterialIconSet.DULL` +- `GTMaterialIconSet.EMERALD` +- `GTMaterialIconSet.FINE` +- `GTMaterialIconSet.FLINT` +- `GTMaterialIconSet.FLUID` +- `GTMaterialIconSet.GAS` +- `GTMaterialIconSet.GEM_HORIZONTAL` +- `GTMaterialIconSet.GEM_VERTICAL` +- `GTMaterialIconSet.GLASS` +- `GTMaterialIconSet.LAPIS` +- `GTMaterialIconSet.LIGNITE` +- `GTMaterialIconSet.MAGNETIC` +- `GTMaterialIconSet.METALLIC` +- `GTMaterialIconSet.NETHERSTAR` +- `GTMaterialIconSet.OPAL` +- `GTMaterialIconSet.QUARTZ` +- `GTMaterialIconSet.ROUGH` +- `GTMaterialIconSet.RUBY` +- `GTMaterialIconSet.SAND` +- `GTMaterialIconSet.SHINY` +- `GTMaterialIconSet.WOOD` + + +## Custom Icon Sets + +Custom iconsets can be specified as well, using the `gtceu:matieral_icon_set` event: + +```js title="custom_iconsets.js" +GTCEuStartupEvents.registry('gtceu:material_icon_set', event => { + event.create('starry') + .parent('shiny') +}) +``` diff --git a/docs/content/Modpacks/Materials-and-Elements/Material-Properties.md b/docs/content/Modpacks/Materials-and-Elements/Material-Properties.md new file mode 100644 index 0000000000..b9903d34ee --- /dev/null +++ b/docs/content/Modpacks/Materials-and-Elements/Material-Properties.md @@ -0,0 +1,45 @@ +--- +title: Material Properties +--- + + +# Material Properties (WIP) + +```js + +BlastProperty.blastTemp() // (1) +BlastProperty.gasTier() // (2) +BlastProperty.durationOverride() // (3) +BlastProperty.eutOverride() // (4) +- DustProperty: + - .dust() // (5) +- FluidPipeProperty: + - .fluidPipeProperties() // (6) +- FluidProperty: + - .fluid() // (7) + - .isGas() // (8) + - .hasBlock() +- GemProperty: + - .gem() +- IngotProperty: + - .ingot() // (9) + - .smeltInto() + - .arcSmeltInto() + - .magneticMaterial() + - .macerateInto() +- OreProperty: + - .ore() // (10) +``` + +1. Sets the Blast Furnace Temperature of the material. If the temperature is below 1000K recipes will be generated in the Primitive Blast Furnace. If above 1750K recipes for the Hot Ingot will be created along with the Vacuum Freezer Recipe to cool the ingot. Example: `.blastTemp(2750)` + +2. Sets the Gas Tier which determins what GAS EBF recipes will be generated. Example: `.gasTier(LOW)` + +3. Overrides the EBF's default behaviour for recipe durations. + +4. Overrides the EBF's default behaviour for EU/t. + +5. Used for creating a dust material. The haverst level and burn time can be specified in the brackets. Example: `.dust(2, 4000)` + +6. Creates a fluid pipe out of the material it is added to. The possible values are: Max Fluid Temperature, Throughput, Gas Proof, Acid Proof, Cyro Proof, Plasma Proof, + Channels. Example: `.fluidPipeProperties(9620, 850, false, false, false, false, 1)` diff --git a/docs/content/Modpacks/Materials-and-Elements/Modifying-Existing-Materials.md b/docs/content/Modpacks/Materials-and-Elements/Modifying-Existing-Materials.md new file mode 100644 index 0000000000..20236fdb9e --- /dev/null +++ b/docs/content/Modpacks/Materials-and-Elements/Modifying-Existing-Materials.md @@ -0,0 +1,84 @@ +--- +title: Modifying Existing Materials +--- + + +# Modifying Existing Materials + +All periodic table elements are present in GT, but some of them don't have any properties attached. You can also add a BlastProperty for EBF autogenerated recipes. You can also do this for other materials such as Obsidian. Here is how you can add them: + +```js title="periodic_table_elements.js" + const $IngotProperty = Java.loadClass('com.gregtechceu.gtceu.api.data.chemical.material.properties.IngotProperty'); + const $DustProperty = Java.loadClass('com.gregtechceu.gtceu.api.data.chemical.material.properties.DustProperty'); + const $BlastProperty = Java.loadClass('com.gregtechceu.gtceu.api.data.chemical.material.properties.BlastProperty'); + + GTCEuStartupEvents.registry('gtceu:material', event => { + + // Ingot + GTMaterials.Zirconium.setProperty(PropertyKey.INGOT, new $IngotProperty()); + GTMaterials.Obsidian.setProperty(PropertyKey.INGOT, new $IngotProperty()); + + // Dust + GTMaterials.Selenium.setProperty(PropertyKey.DUST, new $DustProperty()); + + // Blast Property + GTMaterials.Zirconium.setProperty(PropertyKey.BLAST, new $BlastProperty(8000, 'higher', GTValues.VA(GTValues.MV), 8000)); + + }); +``` + +Adding fluids to existing materials requires a bit of work with the new FluidStorage system + +```js title="fluid_property.js" + +const $FluidProperty = Java.loadClass('com.gregtechceu.gtceu.api.data.chemical.material.properties.FluidProperty'); +const $FluidBuilder = Java.loadClass('com.gregtechceu.gtceu.api.fluids.FluidBuilder'); +const $FluidStorageKeys = Java.loadClass('com.gregtechceu.gtceu.api.fluids.store.FluidStorageKeys'); + +GTCEuStartupEvents.registry('gtceu:material', event => { + addFluid(GTMaterials.Iodine, $FluidStorageKeys.LIQUID); // Can be LIQUID, GAS, PLASMA or MOLTEN + addFluid(GTMaterials.Oganesson, $FluidStorageKeys.GAS); +} + + +let addFluid = (mat, key) => { + let prop = new $FluidProperty(); + prop.getStorage().enqueueRegistration(key, new $FluidBuilder()); + mat.setProperty(PropertyKey.FLUID, prop); +} +``` + +You can even add an ore to existing materials: + +```js title="ore_property.js" + GTCEuStartupEvents.registry('gtceu:material', event => { + + const $OreProperty = Java.loadClass('com.gregtechceu.gtceu.api.data.chemical.material.properties.OreProperty'); + + // Zinc Ore + GTMaterials.Zinc.setProperty(PropertyKey.ORE, new $OreProperty()); + + }); +``` + +You can also add flags to existing materials: + +```js title="flags.js" + GTCEuStartupEvents.registry('gtceu:material', event => { + + GTMaterials.Lead.addFlags(GTMaterialFlags.GENERATE_GEAR); // This is for materials already in GTCEU + GTMaterials.get('custom_material_name').addFlags(GTMaterialFlags.GENERATE_FOIL); // This only works for materials added by GTCEU addons + + }); +``` + +Editing the color of an existing material: + + +```js title="material_modification.js" + GTCEuStartupEvents.materialModification(event => { + GTMaterials.BismuthBronze.setMaterialARGB(0x82AD92) //(1) + }) +``` + +1. Most methods in the [``Material`` class](https://github.com/GregTechCEu/GregTech-Modern/blob/1.20.1/src/main/java/com/gregtechceu/gtceu/api/data/chemical/material/Material.java) can be used in the ``materialModification`` event \ No newline at end of file diff --git a/docs/content/Modpacks/Materials-and-Elements/TagPrefixes-and-the-power-of-.setIgnored().md b/docs/content/Modpacks/Materials-and-Elements/TagPrefixes-and-the-power-of-.setIgnored().md new file mode 100644 index 0000000000..f13de40887 --- /dev/null +++ b/docs/content/Modpacks/Materials-and-Elements/TagPrefixes-and-the-power-of-.setIgnored().md @@ -0,0 +1,46 @@ +--- +title: TagPrefixes and the power of .setIgnored() +--- +## What is a TagPrefix? +TagPrefixes are GTCEu Modern's way of streamlining applying item and block tags to Materials, along with some other functions. The `TagPrefix` class, +available for use in startup and server scripts, contains a number of predefined TagPrefixes that +associate potentially everything from drill heads to flawless gemstones with a material. A common and easy-to-understand example is `TagPrefix.ingot`, +which associates with all Materials that have an IngotProperty and thus an associated ingot item, including custom ones defined via KubeJS. +TagPrefixes provide localization, item and block tagging, and influence many crafting recipes, and are integral to the functioning of GTCEu's material definition system. + +!!! tip "What TagPrefixes are there?" + A list of all availabe TagPrefixes can be found in GTCEu Modern's GitHub or in the .JAR file, in the class `TagPrefix`. + +## What is .setIgnored()? + +While trawling thorugh GTCEu Modern's codebase, or simply by playing Minecraft, you may have noticed that GTCEu Modern treats some vanilla materials differently. +For example, iron ingots are a vanilla item, yet GTCEu Modern does not create a duplicate iron ingot, as its Material definition would suggest it is meant to do. +Instead, the GTCEu Modern Material entry for iron treats the vanilla iron ingot as the material's ingot, and thus produces no duplicates. +This functionality is governed by TagPrefixes, and can also be harnessed by packmakers for their own custom items, or when writing compatibility between GTCEu Modern and another mod. + +## Okay, but how do I use this? + +This functionality can be leveraged in the material modification event, which is a startup event. +The material modification event occurs in Minecraft's boot sequence after Material registration is finalized, but before the Material registry is closed; you won't be able to define any new Materials using it. +The following calls are available for each TagPrefix: + - `.setIgnored()` with one input parameter: Takes a `Material` as input and prevents GTCEu from associating that specific TagPrefix with that Material. + - `.setIgnored()` with two input parameters: Takes a `Material` and an `Item` or `Block` (or any class that that implements the `ItemLike` interface) as input; causes GTCEu to treat the passed `ItemLike` as whatever item the TagPrefix would have originally generated for the Material. An `ItemLike...` varargs in the form of a JS array may also be passed to perform the action on multiple blocks and/or items at once. + - `.removeIgnored()`: takes a `Material` as input and re-enables generation of the item associated with the TagPrefix for that material. + +!!! caution "Beware of `Item.of()`!" The classic way to retrieve an `Item` in KubeJS, namely the `Item.of()` wrapper, doesn't work here. You will need to directly pass an `ItemLike` from a Java class for `.setIgnored()` to work correctly. + +A more illustrative example, using some Applied Energistics 2 items: + + ```js title="setignored_usage_example.js" + GTCEuStartupEvents.materialModification(event => { // (1) + TagPrefix.gemChipped.setIgnored(GTMaterialRegistry.getMaterial("fluix_crystal")) // (2) + TagPrefix.rock.setIgnored(GTMaterialRegistry.getMaterial("sky_stone"), AEBlocks.SKY_STONE_BLOCK) // (3) + TagPrefix.ingot.removeIgnored(GTMaterials.Iron) // (4) + }) + ``` + 1. This event has no methods such as `event.create()`, as it is not intended to be used to create anything, only tweak pre-existing Material associations. In fact, this event has no accessible methods whatsoever. + 2. This call prevents GTCEu Modern from creating a chipped gem variant of the custom 'fluix_crystal' Material. + 3. This call makes GTCEu Modern associate AE2's Sky Stone block as a rock type (like vanilla stone is associated with the GTCEu Modern stone `Material`) with the custom 'sky_stone' Material. It may be necessary to manually load whatever data definition class contains the `ItemLike` you wish to associate with your `Material`, depending on how the mod's author has provided KubeJS access to the modn's classes. + 4. This call makes GTCEu Modern de-associate vanilla iron ingots from GTCEu Modern's iron Material entry, causing it to generate a duplicate iron ingot. + +The `Material` for which you are adjusting the TagPrefix must be registered in GTCEu Modern's material registry; if this material is custom, this is done using `GTCEuStartupEvents.registry()`, as depicted in these docs. diff --git a/docs/content/Modpacks/Materials-and-Elements/The-ChemicalHelper-class.md b/docs/content/Modpacks/Materials-and-Elements/The-ChemicalHelper-class.md new file mode 100644 index 0000000000..8234f5be7c --- /dev/null +++ b/docs/content/Modpacks/Materials-and-Elements/The-ChemicalHelper-class.md @@ -0,0 +1,44 @@ +--- +title: The ChemicalHelper class +--- + +It may behoove a packmaker working with GTCEu Modern to learn about the ChemicalHelper class. This class, available for use in server scripts, +contains a number of useful methods that can ease working with GTCEu Materials in contexts where it might not be possible, or it might be unsafe, to work with item or block tags. + +## Useful functions that ChemicalHelper offers +The following functions are available for use by packmakers: + - `.getMaterial()`: can take almost any form of item reference (`Item`, `ItemStack`, `Ingredient` and so on) and will return the `Material` entry associated with it. +If there is no associated Material, the method returns `null`. A `Fluid` may also be passed as input. + - `.getPrefix()`: Takes an item reference as input and will return the TagPrefix it is associated with it. If there is non associated, the method will return `null`. + - `.getIngot()` + - `.getDust()` + - These two methods take two parameters each as input: a `Material`, and a number representing a material amount, and will return an ItemStack representation of the respective Material's dust or ingot form, if it has one. + The material amount is usually very large; it is generally an integer multiple or fraction of the predefined value `GTValues.M`, which is the commonly agreed-upon material amount of one (1) ingot or regular dust. + Depending on the amount passed, the functions will return different items: `.getIngot()`, for example, will return + an ItemStack representation of a block or nugget of the associated Material if the passed amount is large or small enough. + `.getDust()`, in similar fashion, will return regular, small or tiny dust ItemStack representations depending on the material amount passed. + - `.getTag()` + - `.getBlockTag()` + - `.getTags()` + - `.getBlockTags()` + - Takes a `TagPrefix` and a non-`null` `Material` as input and returns the first item or block tag + (or a Java array of all item or block tags if the plural functions are used) possessed by the item represented by that `TagPrefix`-`Material` combination. + - `.get()`: Takes a `TagPrefix`, a `Material` and optionally an item count that otherwise defaults to 1, and returns an ItemStack representing that `TagPrefix`-`Material` combination with the specified item count. + +Some usage examples: + +```js title="chemicalhelper_example_script.js" +var ironMaterial = ChemicalHelper.getMaterial(Item.of("gtceu:double_iron_plate").asItem()) // (1) +var rawOrePrefix = ChemicalHelper.getPrefix(Item.of("gtceu:raw_platinum").asItem()) // (2) +var cobaltIngotStack = ChemicalHelper.get(TagPrefix.ingot, GTMaterials.Cobalt, 32) // (3) + +var goldNugget = ChemicalHelper.getIngot(GTMaterials.Gold, GTValues.M / 9)// (4) +var steelBlock = ChemicalHelper.getIngot(GTMaterials.Steel, GTValues.M * 9) + +var ashSmallDust = ChemicalHelper.getDust(GTMaterials.Ash, GTValues.M / 4)// (5) +``` +1. `ironMaterial` is now a reference to `GTMaterials.Iron`. +2. `rawOrePrefix` is now a reference to `TagPrefix.rawOre`. +3. `cobaltIngotStack` is now an ItemStack representing half a stack of cobalt ingots. +4. `goldNugget` is now an ItemStack representing one gold nugget. +5. `ashSmallDust` is now an ItemStack representing a small ash pile. diff --git a/docs/content/Modpacks/Materials-and-Elements/index.md b/docs/content/Modpacks/Materials-and-Elements/index.md new file mode 100644 index 0000000000..b062b01db4 --- /dev/null +++ b/docs/content/Modpacks/Materials-and-Elements/index.md @@ -0,0 +1,14 @@ +--- +title: "Materials & Elements" +--- + + +# Materials & Elements + +GregTech has its own material system based on chemical elements. + +Materials are composed of chemical elements and/or other materials. +Each material can have different items (and blocks), such as ingots, dusts, plates, wires, ores, etc. + +## A note about registration +Order matters when you are registering a new material. If you reference a material by `.components()`, you must make sure the other material(s) have been created before the current one. diff --git a/docs/content/Modpacks/Ore-Generation/01-Customizing-Veins.md b/docs/content/Modpacks/Ore-Generation/01-Customizing-Veins.md new file mode 100644 index 0000000000..3edf41b11c --- /dev/null +++ b/docs/content/Modpacks/Ore-Generation/01-Customizing-Veins.md @@ -0,0 +1,143 @@ +--- +title: "Customizing Veins" +--- + + +# Creating and Modifying Ore Veins + +You can create your own custom ore veins using KJS. +It is also possible to modify or even delete existing ones. + + +## Creating New Veins + +```js title="server_scripts/custom_ore_vein.js" +GTCEuServerEvents.oreVeins(event => { + event.add("kubejs:custom_vein", vein => { + // Basic vein generation properties + vein.weight(200) // [*] (1) + vein.clusterSize(40) // [*] (2) + vein.density(0.25) // [*] (3) + vein.discardChanceOnAirExposure(0) // (4) + + // Define where the vein can generate + vein.layer("deepslate") // [*] (5) + vein.dimensions("minecraft:overworld") // (6) + vein.biomes("#minecraft:is_overworld") // (7) + + // Define a height range: + // You must choose EXACTLY ONE of these options! [*] + vein.heightRangeUniform(-60, 20) // (8) + vein.heightRangeTriangle(-60, 20) // (9) + vein.heightRange(/* ... */) // (10) + + // Define the vein's generator: + vein.generator(/* ... */) // [*] (11) + + // Add one or more type of surface indicator to the vein: + vein.addIndicator(/* ... */) // (12) + }) +}) +``` + +1. An ore vein's weight determines the chance of it being chosen over another vein type, to be generated at a possible vein location. + The higher the weight, the more frequently an ore vein type will be generated. +2. Cluster size determines the diameter of an ore vein. +3. The density determines how frequently ores occur inside the vein. +4. Determines the chance of an ore block being skipped when it is exposed to air. Must be between `0` and `1`. + **Default:** `0` +5. See [Layers & Dimensions](./04-Layers-and-Dimensions.md) +6. Limits vein generation to the supplied dimensions. Note that these vein's layer must be applicable for them. + **Default:** All dimensions of the vein's layer. +
+ _Accepts any number of parameters._ +7. Determines the biome (or biome tag) the vein can generate in. + **Default:** If no biome is explicitly set, the vein will generate in any biome. +
+ _Accepts either a single biome tag (prefixed with `#`), or any number of individual biomes._ +8. Uniformly distributed across the height range +9. Biased towards the center of the height range +10. You can also use Minecraft's `HeightRangePlacement` directly, instead of the above shorthand versions: + ```js + vein.heightRange( + height: { + type: "uniform", + min_inclusive: { + absolute: -60 + }, + max_inclusive: { + absolute: 20 + } + }) + ``` +11. See [Generators](./02-Generators.md#vein-generators) for a list of available generators. +12. See [Generators](./02-Generators.md#indicator-generators) for a list of available generators. + + +??? example "Creating a new biome tag for your ore vein" + In case you want to limit your ore vein to multiple biomes that don't have a common tag yet, you can either specify all biomes manually, or you can create a biome tag: + + ```js title="server_scripts/biome_tags.js" + ServerEvents.tags('biome', event => { + event.add('kubejs:my_biome_tag', 'minecraft:forest') + event.add('kubejs:my_biome_tag', 'minecraft:river') + }) + ``` + + You can then use your biome tag by simply calling `vein.biomes('#kubejs:my_biome_tag')` in your vein definition. + + +## Removing an Existing Ore Vein + +```js title="server_scripts/remove_ore_vein.js" +GTCEuServerEvents.oreVeins(event => { + event.remove("gtceu:magnetite_vein_ow") +}) +``` + + +??? example "Removing all ore veins" + If you want to remove **all** predefined ore veins (for example if you want to completely change ore generation + in your modpack), you can use the following code: + + ```js + GTCEuServerEvents.oreVeins(event => { + event.removeAll() + }) + ``` + + You can also filter the veins you want to remove: + + ```js + event.removeAll((id, vein) => id.path != "magnetite_vein_ow") + ``` + + +## Modifying Existing Veins + +```js title="server_scripts/modify_ore_vein.js" +GTCEuServerEvents.oreVeins(event => { + event.modify("gtceu:cassiterite_vein", vein => { + vein.density(1.0) + }) +}) +``` + +The API for vein modifications is the same as for creating new veins. + + +!!! warning "Moving veins to other dimensions" + When moving one of the default veins to another dimension, keep in mind that you also have to change their biome(s) accordingly. + + +??? example "Modifying ALL existing veins" + You can also modify all existing ore veins at once: + + ```js title="server_scripts/modify_all_veins.js" + GTCEuServerEvents.oreVeins(event => { + event.modifyAll((id, vein) => { + console.log("Modifying vein: " + id) + vein.density(1.0) + }) + }) + ``` diff --git a/docs/content/Modpacks/Ore-Generation/02-Generators.md b/docs/content/Modpacks/Ore-Generation/02-Generators.md new file mode 100644 index 0000000000..caaf7558f7 --- /dev/null +++ b/docs/content/Modpacks/Ore-Generation/02-Generators.md @@ -0,0 +1,137 @@ +--- +title: "Generators" +--- + + +# Vein & Indicator Generators + +## Vein Generators + + +### Layered Vein Generator + +```js +vein.layeredVeinGenerator(generator => generator + .buildLayerPattern(pattern => pattern + .layer(l => l.weight(3).mat(GTMaterials.Silver).size(2, 4)) + .layer(l => l.weight(2).mat(GTMaterials.Gold).size(1, 1)) + .layer(l => l.weight(1).block(() => Block.getBlock('minecraft:oak_log')).size(1, 1)) + .layer(l => l.weight(1).state(() => Block.getBlock('minecraft:oak_planks').defaultBlockState()).size(1, 1)) + ) +) +``` + + +### Veined Vein Generator + +```js +vein.veinedVeinGenerator(generator => generator + .oreBlock(GTMaterials.Silver, 4) // (1) + .rareBlock(GTMaterials.Gold, 1) // (2) + .rareBlockChance(0.25) + .veininessThreshold(0.1) + .maxRichnessThreshold(0.3) + .minRichness(0.3) + .maxRichness(0.5) + .edgeRoundoffBegin(10) // (3) + .maxEdgeRoundoff(0.2) // (4) +) +``` + +1. **Param 1:** Either a material or block state + **Param 2:** Generation weight +2. **Param 1:** Either a material or block state + **Param 2:** Generation weight +3. Determines how much the veins become thinner towards their ends +4. Determines how much the veins become thinner towards their ends + + +!!! info "Noise Parameters" + The vein's noise parameters can be summarized as follows: + - `veininessThreshold` defines how "sharp" the edges of the vein are. + Higher values result in more "blurry" edges. + - `maxRichnessThreshold` defines how many ores generate _inside_ the vein (must be `>= veininessThreshold`). + A higher distance between the values results in less "filled" veins. + - `minRichness` and `maxRichness` allow you to limit the output of this calculation to a specific range. + + The output of this calculation determines the chance for each block in the vein to generate. + + +!!! info "Height Ranges" + The height range of the generator is automatically inferred if you use `heightRangeUniform()` or `heightRangeTriangle()` in the vein definition, _before setting the generator_. Otherwise you need to set the height range manually: + + ```js + generator.minYLevel(10) + generator.maxYLevel(90) + ``` + + +### Dike Vein Generator + +```js +vein.dikeVeinGenerator(generator => generator + .withBlock(GTMaterials.Silver, 3, 20, 60) // (1) + .withBlock(GTMaterials.Gold, 1, 20, 40) +) +``` + +1. **Param 1:** Either a material or block state + **Param 2:** Generation weight + **Param 3:** Min Y Position + **Param 4:** Max Y Position + +!!! info "Height Ranges" + The height range of the generator is automatically inferred if you use `heightRangeUniform()` or `heightRangeTriangle()` in the vein definition, _before setting the generator_. Otherwise you need to set the height range manually: + + ```js + generator.minYLevel(10) + generator.maxYLevel(90) + ``` + + +### Standard Vein Generator + +!!! failure "Not yet documented" + +```js +vein.standardVeinGenerator(generator => /* ... */) +``` + + +### Geode Vein Generator + +!!! failure "Not yet documented" + +```js +vein.geodeVeinGenerator(generator => /* ... */) +``` + + +## Indicator Generators + + +### Surface Rock Indicators + +```js +vein.surfaceIndicatorGenerator(indicator => indicator + .surfaceRock(GTMaterials.Platinum) // [*] (1) + .placement("above") // (2) + .density(0.4) + .radius(5) +) +``` + +1. Instead of a surface rock, you can also define any other block: + ```js + // Using a block: + indicator.block(Block.getBlock('minecraft:oak_log')) + + // Using a block state: + indicator.state(Block.getBlock('minecraft:oak_log').defaultBlockState()) + ``` +2. Valid options: + `surface` generates indicators on the world's surface + `above` generates indicators in the next free space above + `below` generates indicators in the next free space below +
+ **Default:** `surface` \ No newline at end of file diff --git a/docs/content/Modpacks/Ore-Generation/03-Adding-Stone-Types.md b/docs/content/Modpacks/Ore-Generation/03-Adding-Stone-Types.md new file mode 100644 index 0000000000..f8fd7dd880 --- /dev/null +++ b/docs/content/Modpacks/Ore-Generation/03-Adding-Stone-Types.md @@ -0,0 +1,74 @@ +--- +title: "Adding Stone Types" +--- + + +# Adding Stone Types for Ore Blocks + +In a modpack, you may want to add your own stone types in order to integrate GT's ore generation with blocks from other mods. + +To do so, you need to register a tag prefix for your ore, add a language key, as well as allowing your ores to actually generate. + + +## Files + +For example purposes, this guide uses the block "Blockium" (ID: `my_mod:blockium`). +Replace this with the block you want to add ores for. + +```js title="startup_scripts/ore_types.js" +GTCEuStartupEvents.registry('gtceu:tag_prefix', event => { + event.create('blockium', 'ore') // (1) + .stateSupplier(() => Block.getBlock('my_mod:blockium').defaultBlockState()) // (2) + .baseModelLocation('my_mod:block/blockium') // (3) + .unificationEnabled(true) + .materialIconType(GTMaterialIconType.ore) + .generationCondition(ItemGenerationCondition.hasOreProperty) +}) +``` + +1. The first parameter for `create()` is the name that corresponds to your stone type. The second parameter is **always** `'ore'`! +2. For `Block.getBlock()` you must use the stone type's block ID as a parameter. +3. This is the `ResourceLocation` of the base stone type's model. If the base block uses custom rendering, you may need to create your own model. + + +```json title="assets/gtceu/lang/en_us.json" +{ + "tagprefix.blockium": "Blockium %s Ore" +} +``` + + +## Generating the Ores + +To make your ores actually generate in the world, you have several options. +If you just want to stick to ore generation in the default dimensions, the easiest way to achieve this is adding your new ore base blocks to one of the following block tags: + + +- **Overworld:** `minecraft:stone_ore_replaceables` or `minecraft:deepslate_ore_replaceables` +- **Nether:** `minecraft:nether_carver_replaceables` +- **The End:** `forge:end_stone_ore_replaceables` on forge / `c:end_stone_ore_replaceables` on fabric + +??? example "Adding blocks to a tag" + ```js title="server_scripts/ore_type_tags.js" + ServerEvents.tags('block', event => { + event.add('minecraft:stone_ore_replaceables', 'my_mod:blockium') + }) + ``` + +You can also add ores in other dimensions, but to do so you will have to create a custom World Generation Layer. +You'll learn how to do so in [Layers & Dimensions](./04-Layers-and-Dimensions.md) + + +## Non-Default BlockStates + +Some mods may generate blocks with `BlockState`s that differ from their `defaultBlockState`. +In this case you have to specify the actually generated block state in your ore stone type's `stateSupplier`: + +```js +let UtilsJS = Java.loadClass("dev.latvian.mods.kubejs.util.UtilsJS") + +GTCEuStartupEvents.registry('gtceu:tag_prefix', event => { + event.create(type.path, 'ore') + .stateSupplier(() => UtilsJS.parseBlockState("my_mod:blockium[some_blockstate_property=true]")) +}) +``` diff --git a/docs/content/Modpacks/Ore-Generation/04-Layers-and-Dimensions.md b/docs/content/Modpacks/Ore-Generation/04-Layers-and-Dimensions.md new file mode 100644 index 0000000000..ae180ccfe0 --- /dev/null +++ b/docs/content/Modpacks/Ore-Generation/04-Layers-and-Dimensions.md @@ -0,0 +1,35 @@ +--- +title: "Layers & Dimensions" +--- + + +# Layers and Dimensions + + +## Creating a New World Gen Layer + +To create ore veins in another dimension (or just at the location of certain blocks), you need to create a new worldgen layer. +You may also need to add a custom stone type for your ores. + +```js title="startup_scripts/world_gen_layers.js" +GTCEuStartupEvents.registry('gtceu:world_gen_layer', event => { + event.create('my_custom_layer') + .targets('#minecraft:stone_ore_replaceables', 'minecraft:endstone') // [*] (1) + .dimensions('minecraft:overworld', 'minecraft:the_end') // [*] +}) +``` + +1. Accepts tags, blocks and block states. + Also accepts a `RuleTest` or `RuleTestSupplier` in case you need a bit more flexibility. + + +Once the layer is created, you can refer to it by its name when creating or modifying an ore vein: + +```js title="server_scripts/ores.js" +GTCEuServerEvents.oreVeins(event => { + event.add("kubejs:custom_vein", vein => { + vein.layer("my_custom_layer") + // ... + }) +}) +``` diff --git a/docs/content/Modpacks/Ore-Generation/Bedrock-Fluid-Veins.md b/docs/content/Modpacks/Ore-Generation/Bedrock-Fluid-Veins.md new file mode 100644 index 0000000000..4cc7cdd7f5 --- /dev/null +++ b/docs/content/Modpacks/Ore-Generation/Bedrock-Fluid-Veins.md @@ -0,0 +1,27 @@ +--- +title: Bedrock Fluid Veins +--- + + +# Bedrock Fluid Veins + +Bedrock Fluid Veins are invisable veins that exist under the bedrock, to find Fluid Veins you must have at least a HV tier Prospector. A Fluid Drilling Rig must be used to obtain the fluids out of the vein. + +## Creating a Bedrock Fluid Vein + +```js title="fluid_veins.js" +// In server events +GTCEuServerEvents.fluidVeins(event => { + + event.add('gtceu:custom_bedrock_fluid_vein', vein => { + vein.addSpawnDimension('minecraft:overworld') + vein.fluid(() => Fluid.of('gtceu:custom_fluid').fluid) + vein.weight(600) + vein.minimumYield(120) + vein.maximumYield(720) + vein.depletionAmount(2) + vein.depletionChance(1) + vein.depletedYield(50) + }); + +``` \ No newline at end of file diff --git a/docs/content/Modpacks/Ore-Generation/index.md b/docs/content/Modpacks/Ore-Generation/index.md new file mode 100644 index 0000000000..f5db9119f8 --- /dev/null +++ b/docs/content/Modpacks/Ore-Generation/index.md @@ -0,0 +1,20 @@ +--- +title: "Ore Generation" +--- + + +# Ore Generation + +GregTech has its own ore generation that is quite different from that of Minecraft's standard system. +Due to technical limitations, this system does not use Minecraft's features and works a bit differently. + +GT's ores generate in large veins that are placed along a grid (and a random offset per vein) throughout the world. + +In this section, you will learn how to customize ore generation for your modpack. + + +## Reloading Ore Generation + +!!! warning + After changing the ore generation you will have to **restart the server** or **re-open your world**! + It is not enough to simply use `/reload` in this case. diff --git a/docs/content/Modpacks/Other-Topics/Adding-and-Removing-Recipes.md b/docs/content/Modpacks/Other-Topics/Adding-and-Removing-Recipes.md new file mode 100644 index 0000000000..ccea0e46f7 --- /dev/null +++ b/docs/content/Modpacks/Other-Topics/Adding-and-Removing-Recipes.md @@ -0,0 +1,143 @@ +--- +title: "Adding & Removing Recipes" +--- + + +# Adding and Removing Recipes + +## Removing Recipes + +Removing GTCEu Modern recipes with KubeJS works the same as any other recipe, meaning they can be removed by: ID, Mod, Input, Output, Type or a Mixture. + +```js title="gtceu_removal.js" +ServerEvents.recipes(event => { + event.remove({ id: 'gtceu:smelting/sticky_resin_from_slime' }) // (1) + event.remove({ mod: 'gtceu' }) // (2) + event.remove({ type: 'gtceu:arc_furnace' }) // (3) + event.remove({ input: '#forge:ingots/iron' }) // (4) + event.remove({ output: 'minecraft:cobblestone' }) // (5) + event.remove({ type: 'gtceu:assembler', input: '#forge:plates/steel' }) // (6) +}) +``` + +1. Targets the slime to sticky resin furnace recipe only for removal. +2. Targets all recipes under the gtceu mod id for removal. +3. Targets all recipes in the arc furnace for removal. +4. Targets all recipes that have an input of `#forge:ingots/iron` for removal. +5. Targets all recipes that have an output of `minecraft:cobblestone` for removal. +6. Targets all recipes in the gtceu assembler that have an input of `#forge:plates/steel` for removal. + + +## Modifiying Recipes + +With KubeJS it is possible to modify the Inputs or Outputs of existing GTCEu Modern recipes, which uses the same method of targeting the recipes. + +```js title="gtceu_modify.js" +ServerEvents.recipes(event => { + event.replaceInput({ mod: 'gtceu' }, 'minecraft:sand', '#forge:sand') // (1) + event.replaceOutput({ type: 'gtceu:arc_furnace' }, 'gtceu:wrought_iron_ingot', 'minecraft:dirt') // (2) +}) +``` + +1. Targets all gtceu recipes that have and input of `minecraft:sand` and replaces it with `#forge:sand`. +2. Targets all gtceu arc furnace recipes that have an output of `gtceu:wrought_iron_ingot` and replaces it with `minecraft:dirt`. + + +## Adding Recipes + +Syntax: `event.recipes.gtceu.RECIPE_TYPE(string: recipe id)` + +```js title="gtceu_add.js" +ServerEvents.recipes(event => { + event.recipes.gtceu.assembler('test') + .itemInputs( + '64x minecraft:dirt', + '32x minecraft:diamond' + ) + .inputFluids( + Fluid.of('minecraft:lava', 1500) + ) + .itemOutputs( + 'minecraft:stick' + ) + .duration(100) + .EUt(30) +}) +``` + +### Event calls for adding inputs and outputs +- Basic calls: + - `.input()`: The most basic input definition available. Takes two parameters: one RecipeCapability that defines what input type this call is supposed to be (usually an item, a fluid or energy, but can also be, for example, Create Stress Units), and an Object that defines the input itself. Available RecipeCapabilities can be found in the GTCEu Modern GitHub or the mod's .JAR file, but the class containing all of GTCEu Modern's native RecipeCapabilities, `GTRecipeCapabilites`, must be manually loaded in your scripts. This method is unwieldy to use in Javascript; it is more user-friendly to use the ones below that clearly tell you what input type they call. + - `.output()`: As above, but defines an output instead. Takes the exact same parameters. This method is likewise unwieldy to use; it is more user-friendly to use the ones below that clearly tell you what output type they call. +- Inputs: + - Items: + - `.itemInput()` + - `.itemInputs()` + - `.chancedInput()` + - `.notConsumable()` + - Fluids: + - `.inputFluids()` + - `.chancedFluidInput()` + - `.notConsumableFluid()` + - Misc: + - `.circuit()` +- Outputs: + - Items: + - `.itemOutput()` + - `.itemOutputs()` + - `.chancedOutput()` + - Fluids: + - `.outputFluids()` + - `.chancedFluidOutput()` +- Energy: + - `.inputEU()`: Makes the recipe consume a lump sum of EU to start the recipe. Most often seen in fusion reactor recipes. + - `.outputEU()`: Makes the recipe produce a lump sum of EU upon recipe completion. + - `.EUt()`: Takes a numerical value represesnting an EU amount. Positive values will make the recipe consume energy per tick, negative ones will make it generate energy per tick. +- More granular functionality: + - `.perTick()`: Using this will enable you to control whether a recipe input/output is consumed/produced per tick the recipe is running or all at once at recipe start/end. Set to true with `.perTick(true)` to make the recipe builder consider any following input/output calls as per-tick. Remember to set the value to false with `.perTick(false)` after the calls you intend to be per-tick, to prevent behaviour you don't want! + +### The Research System +GTCEu has Research System which allows for adding extra requirements to recipes such as: Scanner Research, Station Research and Computation. + +```js title="scanner_research.js" +ServerEvents.recipes(event => { + event.recipes.gtceu.assembly_line('scanner_test') + .itemInputs('64x minecraft:coal') + .itemOutputs('minecraft:diamond') + .duration(10000) + .EUt(GTValues.VA[GTValues.IV]) + ["scannerResearch(java.util.function.UnaryOperator)"](b => b.researchStack(Item.of('minecraft:coal_block')).EUt(GTValues.VA[GTValues.IV]).duration(420)) // (1) +}) +``` + +1. Note due to a `JS Moment` you have to force `scannerResearch` to be interpreted by Rhino in a specific way. Scanner Research accepts an `ItemStack` input in the `.researchStack()` object, and you can also define the `EUt` and `Duration` outside of the `.researchStack()` object. + +```js title="station_research" +ServerEvents.recipes(event => { + event.recipes.gtceu.assembly_line('station_test') + .itemInputs('64x minecraft:coal') + .itemOutputs('minecraft:diamond') + .duration(10000) + .EUt(GTValues.VA[GTValues.IV]) + .stationResearch(b => b.researchStack(Item.of('minecraft:coal_block')).EUt(GTValues.VA[GTValues.IV]).CWUt(10)) // (1) +}) +``` + +1. Just like `Scanner Research` `Station Research` accepts an `ItemStack` input in the `.researchStack()` object, however you can only define `EUt` and `CWUt` outside of the `.researchStack()` object. `CWUt` is used to define the duration of the `Station Research` recipe. + +### Rock breaker fluids + +!!! warning + When adding rock breaker recipes you will need to manually define the fluids the rock breaker will use. (might change in the future) + +```js title="rock_breaker.js" +ServerEvents.recipes(event => { + event.recipes.gtceu.rock_breaker('rhino_jank') + .notConsumable('minecraft:dirt') + .itemOutputs('minecraft:dirt') + .addDataString("fluidA", "minecraft:lava") + .addDataString("fluidB", "minecraft:water") + .duration(16) + .EUt(30) +}) +``` diff --git a/docs/content/Modpacks/Other-Topics/Custom-Coils.md b/docs/content/Modpacks/Other-Topics/Custom-Coils.md new file mode 100644 index 0000000000..36308e4690 --- /dev/null +++ b/docs/content/Modpacks/Other-Topics/Custom-Coils.md @@ -0,0 +1,23 @@ +--- +title: Custom Coils +--- + + +## Coil Creation + +```js +StartupEvents.registry('block', event => { + event.create('infinity_coil_block', 'gtceu:coil') + .temperature(100) + .level(0) + .energyDiscount(1) // (1) + .tier(10) + .coilMaterial(() => GTMaterials.get('infinity')) + .texture('kubejs:block/example_block') + .hardness(5) + .requiresTool(true) + .material('metal') +}) +``` + +1. The Energy Discount must be at least 1. diff --git a/docs/content/Modpacks/Other-Topics/Custom-Machines.md b/docs/content/Modpacks/Other-Topics/Custom-Machines.md new file mode 100644 index 0000000000..4f81fb848f --- /dev/null +++ b/docs/content/Modpacks/Other-Topics/Custom-Machines.md @@ -0,0 +1,134 @@ +--- +title: Custom Machines +--- + + +# Custom Machines + + +## Creating Custom Steam Machine + +```js title="test_steam_machine.js" +GTCEuStartupEvents.registry('gtceu:machine', event => { + event.create('test_simple_steam_machine', 'steam', true) // (1) +}) +``` + +1. Machine ID, Machine Type, Has High Pressure Varient + + +## Creating Custom Electric Machine + +```js title="test_electric_machine.js" +GTCEuStartupEvents.registry('gtceu:machine', event => { + event.create('test_electric', 'simple', 0, GTValues.LV, GTValues.MV, GTValues.HV) // (1) + .rotationState(RotationState.NON_Y_AXIS) + .recipeType('test_recipe_type') + .tankScalingFunction(tier => tier * 3200) +}) +``` + + +1. Machine ID, Machine Type, Pollution Produced, Voltage Tiers + + + +## Creating Custom Kinetic Machine + +```js title="test_kinetic_machine.js" +GTCEuStartupEvents.registry('gtceu:machine', event => { + event.create('test_kinetic', 'kinetic', GTValues.LV, GTValues.MV, GTValues.HV) + .rotationState(RotationState.NON_Y_AXIS) + .recipeType('test_kinetic_recipe_type') + .tankScalingFunction(tier => tier * 3200) +}) +``` + + +## Creating Custom Generator + +```js title="test_generator.js" +GTCEuStartupEvents.registry('gtceu:machine', event => { + event.create('test_generator', 'generator', GTValues.LV, GTValues.MV, GTValues.HV) // (1) + .recipeType('test_generator_recipe_type') + .tankScalingFunction(tier => tier * 3200) +}) +``` + + +## Creating Custom Multiblock + +```js title="test_multiblock.js" +GTCEuStartupEvents.registry('gtceu:machine', event => { + event.create('test_generator', 'multiblock') + .tooltips(Component.translatable('your.langfile.entry.here')) // (1) + .rotationState(RotationState.NON_Y_AXIS) + .appearanceBlock(GTBlocks.CASING_STEEL_SOLID) + .recipeTypes(['test_recipe_type_1', 'test_recipe_type_2']) + .pattern(definition => FactoryBlockPattern.start() + .aisle('CCC', 'GGG', 'CCC') + .aisle('CCC', 'GDG', 'CSC') + .aisle('CKC', 'GGG', 'CMC') + .where('K', Predicates.controller(Predicates.blocks(definition.get()))) + .where('M', Predicates.abilities(PartAbility.MAINTENANCE)) + .where('S', Predicates.abilities(PartAbility.MUFFLER)) + .where('D', Predicates.blocks(GTBlocks.COIL_CUPRONICKEL.get())) + .where('G', Predicates.blocks('minecraft:glass')) + .where('C', Predicates.blocks(GTBlocks.CASING_STEEL_SOLID.get()) + .or(Predicates.autoAbilities(definition.getRecipeTypes()))) + .build()) + .workableCasingRenderer( + "gtceu:block/casings/solid/machine_casing_inert_ptfe", + "gtceu:block/multiblock/large_chemical_reactor", + false + ) +}) +``` + + +1. You can add tooltips to your multiblock controllers that show up when you mouseover them. Each separate call of ```.tooltips()``` will add a separate line to the controller's tooltip. ```Component.translatable()``` reads entries from .json lang files placed in ```kubejs/assets/gtceu/lang``` or supplied via a standalone resource pack. The ```Component``` class is autoloaded by KubeJS at compile time; it doesn't need to be manually loaded. + + +### Shape Info + +Shape Info is used to manually define how your multiblock appears in the JEI/REI/EMI multiblock preview tab. + +```js title="shape_info_test.js" +GTCEuStartupEvents.registry('gtceu:machine', event => { + event.create('test_generator', 'multiblock') + .rotationState(RotationState.NON_Y_AXIS) + .appearanceBlock(GTBlocks.CASING_STEEL_SOLID) + .recipeTypes(['test_recipe_type_1', 'test_recipe_type_2']) + .pattern(definition => FactoryBlockPattern.start() + .aisle('CCC', 'GGG', 'CCC') + .aisle('CCC', 'GDG', 'CSC') + .aisle('CKC', 'GGG', 'CMC') + .where('K', Predicates.controller(Predicates.blocks(definition.get()))) + .where('M', Predicates.abilities(PartAbility.MAINTENANCE)) + .where('S', Predicates.abilities(PartAbility.MUFFLER)) + .where('D', Predicates.blocks(GTBlocks.COIL_CUPRONICKEL.get())) + .where('G', Predicates.blocks('minecraft:glass')) + .where('C', Predicates.blocks(GTBlocks.CASING_STEEL_SOLID.get()) + .or(Predicates.autoAbilities(definition.getRecipeTypes()))) + .build()) + .shapeInfo(controller => MultiblockShapeInfo.builder() + .aisle('eCe', 'GGG', 'CCC') + .aisle('CCC', 'GDG', 'CSC') + .aisle('iKo', 'GGG', 'CMC') + .where('K', controller, Direction.SOUTH) + .where('C', GTBlocks.CASING_STEEL_SOLID.get()) + .where('G', Block.getBlock('minecraft:glass')) + .where('D', GTBlocks.COIL_CUPRONICKEL.get()) + .where('S', GTMachines.MUFFLER_HATCH[1], Direction.UP) + .where('M', GTMachines.MAINTENANCE_HATCH[1], Direction.SOUTH) + .where('e', GTMachines.ENERGY_INPUT_HATCH[1], Direction.NORTH) + .where('i', GTMachines.ITEM_IMPORT_BUS[1], Direction.SOUTH) + .where('0', GTMachines.ITEM_EXPORT_BUS[1], Direction.SOUTH) + .build()) + .workableCasingRenderer( + "gtceu:block/casings/solid/machine_casing_inert_ptfe", + "gtceu:block/multiblock/large_chemical_reactor", + false + ) +}) +``` diff --git a/docs/content/Modpacks/Other-Topics/Custom-Recipe-Types.md b/docs/content/Modpacks/Other-Topics/Custom-Recipe-Types.md new file mode 100644 index 0000000000..26ec1c6c19 --- /dev/null +++ b/docs/content/Modpacks/Other-Topics/Custom-Recipe-Types.md @@ -0,0 +1,41 @@ +--- +title: Custom Recipe Type +--- + + +## Creating a Recipe Type + +!!! important "Recipe Types MUST be registered before the machines or multiblocks" + +```js title="test_recipe_type.js" +GTCEuStartupEvents.registry('gtceu:recipe_type', event => { + event.create('test_recipe_type') + .category('test') + .recipeModifiers([GTRecipeModifiers.PARALLEL_HATCH, GTRecipeModifiers.ELECTRIC_OVERCLOCK.apply(OverclockingLogic.PERFECT_OVERCLOCK)]) // (1) + .setEUIO('in') + .setMaxIOSize(3, 3, 3, 3) // (2) + .setSlotOverlay(false, false, GuiTextures.SOLIDIFIER_OVERLAY) + .setProgressBar(GuiTextures.PROGRESS_BAR_ARROW, FillDirection.LEFT_TO_RIGHT) // (3) + .setSound(GTSoundEntries.COOLING) +}) +``` + +1. If electric and/or multiblock machines can process your custom recipe type, ```.recipeModifiers()``` will allow you to fine-tune the behaviour of these machine when running recipes of your custom recipe type. ```GTRecipeModifiers.PARALLEL_HATCH``` will enable Multiblock Machines to parallelize recipes of your custom type via an optional Parallel Hatch, while ```GTRecipeModifiers.ELECTRIC_OVERCLOCK```will define how your recipes overclock in electric machines and multiblocks. +2. Max Item Inputs, Max Item Outputs, Max Fluid Inputs, Max Fluid Outputs +3. A list of available ```GuiTextures``` and ```FillDirection```s can be found in the GTCEu Modern Github, or in the .jar file. + + +```js title="test_kinetic_recipe_type.js" +GTCEuStartupEvents.registry('gtceu:recipe_type', event => { + event.create('test_recipe_type') + .category('test_kinetic') + .setEUIO('in') + .setMaxIOSize(3, 3, 3, 3) + .setSlotOverlay(false, false, GuiTextures.SOLIDIFIER_OVERLAY) + .setProgressBar(GuiTextures.PROGRESS_BAR_ARROW, FillDirection.LEFT_TO_RIGHT) + .setSound(GTSoundEntries.COOLING) + .setMaxTooltip(6) // (1) +}) +``` + +1. Helps to adjust the JEI/REI/EMI layout when adding addition conditions to the recipe like RPM and SU. diff --git a/docs/content/Modpacks/Other-Topics/LDLib-UI-Editor.md b/docs/content/Modpacks/Other-Topics/LDLib-UI-Editor.md new file mode 100644 index 0000000000..3463384262 --- /dev/null +++ b/docs/content/Modpacks/Other-Topics/LDLib-UI-Editor.md @@ -0,0 +1,10 @@ +--- +title: LDLib UI Editor +--- + + +# LDLib UI Editor + +Low Drag Lib (bundled with GTCEu Modern) provides an in-game UI Editor for creating custom GUI's for +machines and for Recipe Types. The UI Editor is accessible in-game +via the command `/gtceu ui_editor`. \ No newline at end of file diff --git a/docs/content/Modpacks/Other-Topics/Modifying-Crafting-Components.md b/docs/content/Modpacks/Other-Topics/Modifying-Crafting-Components.md new file mode 100644 index 0000000000..8dc2e2e7f3 --- /dev/null +++ b/docs/content/Modpacks/Other-Topics/Modifying-Crafting-Components.md @@ -0,0 +1,150 @@ +--- +title: "Modifying Crafting Components" +--- + +# Modifying Crafting Components + +## Changing a single entry + +With KubeJS it is possible to modify the predefined components of existing GTCEu Modern machine crafting recipes. +You can replace singular entries, or do bulk modification of components. +-1 is defined as a fallback value if no other entries exist. if you modify in bulk, you **MUST** insert one if the component expects it to exist. otherwise the game may crash when loading recipes. Other numbers correspond to tier indices. for example: `3 == GTValues.HV` + +```js title="modification.js" +const Map = Java.loadClass("java.util.Map") + +GTCEuServerEvents.craftingComponents(event => { + event.modifyItem(CraftingComponent.CIRCUIT, GTValues.MV, Item.of('minecraft:dirt')) // (1) + event.modifyItem(CraftingComponent.PUMP, Map.of( + GTValues.FALLBACK, Item.of('gtceu:lv_robot_arm'), + GTValues.LV, Item.of('gtceu:lv_robot_arm'), + GTValues.MV, Item.of('gtceu:mv_robot_arm'), + GTValues.HV, Item.of('gtceu:hv_robot_arm'), + GTValues.EV, Item.of('gtceu:ev_robot_arm'), + GTValues.IV, Item.of('gtceu:iv_robot_arm'), + GTValues.LuV, Item.of('gtceu:luv_robot_arm'), + GTValues.ZPM, Item.of('gtceu:zpm_robot_arm'), + GTValues.UV, Item.of('gtceu:uv_robot_arm'), + )) // (2) + event.modifyTag(CraftingComponent.CASING, GTValues.EV, 'minecraft:logs') // (3) + event.modifyUnificationEntry(CraftingComponent.PLATE, GTValues.UEV, new UnificationEntry('plate', 'gtceu:infinity')) // (4) +}) +``` + +1. Replaces the MV circuit tag in all GT machine crafting recipes with a single block of `minecraft:dirt`. +2. Modifies all pumps in GT machine crafting recipes by replacing the pump with a robot arm, and reinserts the FALLBACK entry. +3. Replaces the EV casing with the `#minecraft:logs` tag. note the lack of `#` at the beginning of the tag! +4. Adds a new entry to the plate component for UEV with prefix `plate` and material `gtceu:infinity`. + +!!! tip "Bulk Callbacks" + + All the bulk callbacks (`modify*` functions that take two parameters instead of three, see example 2) work the same as the single modification functions, except they take a map of `int: value` instead of a single entry. + + +## Creating new components + +It's also possible to create new crafting components with KubeJS. +You must add entries for all tiers you're planning to use, and a FALLBACK if the map *may* be indexed out of bounds at all. +It's recommended to always add a FALLBACK entry. + +```js title="creation.js" +const Map = Java.loadClass("java.util.Map") + +let ITEM_CRAFTING_COMPONENT = null +let TAG_CRAFTING_COMPONENT = null +let UNIFICATION_CRAFTING_COMPONENT = null + +GTCEuServerEvents.craftingComponents(event => { + ITEM_CRAFTING_COMPONENT = event.createItem(Map.of( + GTValues.FALLBACK: Item.of('minecraft:cyan_stained_glass'), + GTValues.LV: Item.of('minecraft:cyan_stained_glass'), + GTValues.MV: Item.of('minecraft:cyan_stained_glass'), + GTValues.HV: Item.of('minecraft:cyan_stained_glass'), + GTValues.EV: Item.of('minecraft:lime_stained_glass'), + GTValues.IV: Item.of('minecraft:lime_stained_glass'), + GTValues.LuV: Item.of('minecraft:lime_stained_glass'), + GTValues.ZPM: Item.of('minecraft:magenta_stained_glass'), + GTValues.UV: Item.of('minecraft:magenta_stained_glass'), + )) // (1) + TAG_CRAFTING_COMPONENT = event.createTag(Map.of( + GTValues.FALLBACK: 'forge:barrels/wooden' + GTValues.LV: 'forge:chests/wooden' + GTValues.MV: 'forge:chests/trapped' + GTValues.HV: 'forge:chests/ender' + GTValues.EV: 'forge:cobblestone' + GTValues.IV: 'forge:cobblestone/normal' + GTValues.LuV: 'forge:cobblestone/infested' + GTValues.ZPM: 'forge:cobblestone/mossy' + GTValues.UV: 'forge:cobblestone/deepslate' + )) // (2) + UNIFICATION_CRAFTING_COMPONENT = event.createUnificationEntry(Map.of( + GTValues.FALLBACK: new UnificationEntry('plate', 'gtceu:infinity') + GTValues.LV: new UnificationEntry('block', 'gtceu:infinity') + GTValues.MV: new UnificationEntry('ingot', 'gtceu:infinity') + GTValues.HV: new UnificationEntry('dust', 'gtceu:infinity') + GTValues.EV: new UnificationEntry('round', 'gtceu:infinity') + GTValues.IV: new UnificationEntry('foil', 'gtceu:infinity') + GTValues.LuV: new UnificationEntry('longRod', 'gtceu:infinity') + GTValues.ZPM: new UnificationEntry('rod', 'gtceu:infinity') + GTValues.UV: new UnificationEntry('bolt', 'gtceu:infinity') + )) // (3) +}) +``` + +1. Creates a new crafting component with item stack entries. +2. Creates a new crafting component with item tag entries. note the lack of `#` at the beginning of the tag! +3. Creates a new crafting component with UnificationEntry entries. + + +### Builtin Crafting Components + +- `CIRCUIT` +- `BETTER_CIRCUIT` +- `PUMP` +- `WIRE_ELECTRIC` +- `WIRE_QUAD` +- `WIRE_OCT` +- `WIRE_HEX` +- `CABLE` +- `CABLE_DOUBLE` +- `CABLE_QUAD` +- `CABLE_OCT` +- `CABLE_HEX` +- `CABLE_TIER_UP` +- `CABLE_TIER_UP_DOUBLE` +- `CABLE_TIER_UP_QUAD` +- `CABLE_TIER_UP_OCT` +- `CABLE_TIER_UP_HEX` +- `CASING` +- `HULL` +- `PIPE_NORMAL` +- `PIPE_LARGE` +- `PIPE_NONUPLE` +- `GLASS` +- `PLATE` +- `HULL_PLATE` +- `MOTOR` +- `ROTOR` +- `SENSOR` +- `GRINDER` +- `SAWBLADE` +- `DIAMOND` +- `PISTON` +- `EMITTER` +- `CONVEYOR` +- `ROBOT_ARM` +- `COIL_HEATING` +- `COIL_HEATING_DOUBLE` +- `COIL_ELECTRIC` +- `STICK_MAGNETIC` +- `STICK_DISTILLATION` +- `FIELD_GENERATOR` +- `STICK_ELECTROMAGNETIC` +- `STICK_RADIOACTIVE` +- `PIPE_REACTOR` +- `POWER_COMPONENT` +- `VOLTAGE_COIL` +- `SPRING` +- `CRATE` +- `DRUM` +- `FRAME` diff --git a/docs/content/Modpacks/Other-Topics/index.md b/docs/content/Modpacks/Other-Topics/index.md new file mode 100644 index 0000000000..bf20131945 --- /dev/null +++ b/docs/content/Modpacks/Other-Topics/index.md @@ -0,0 +1,8 @@ +--- +title: "Other Topics" +--- + + +# Other Topics + +This section contains other topics that aren't necessarily large enough to be grouped into their own categories. diff --git a/docs/content/Modpacks/index.md b/docs/content/Modpacks/index.md new file mode 100644 index 0000000000..595ebe124a --- /dev/null +++ b/docs/content/Modpacks/index.md @@ -0,0 +1,38 @@ +--- +title: Modpack Creation +--- + + +# Modpack Creation + +GTCEu Modern offers extensive integration with KubeJS for customizability. +Most of our tools for modpack creators revolve around this KubeJS API. + +Refer to this section for information on how to use it, as well as for examples. + + +## General Notes + +Sometimes, calling a specific method is always required when adding (or modifying) something. +These methods are marked with `// [*]` in the docs, like in the following example: + +```js +ServerEvents.exampleEvent(event => { + event.create('example', builder => { + builder.requiredMethod(42) // [*] (1) + builder.otherRequiredMethod(42) // [*] + + builder.optionalMethod() // (2) + }) +}) +``` + +1. These methods are required +2. This method is optional and doesn't have to be called in all cases + + +## Beyond the Docs + +While we try to keep this documentation up to date and as complete as possible, it may not always contain all of the latest information. + +Please also check the [Beyond the Docs](./Beyond-The-Docs.md) page for additional references. diff --git a/docs/content/assets/favicon.png b/docs/content/assets/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..812f49b64465077ac52d49dbd60c3edd01c085a4 GIT binary patch literal 839 zcmV-N1GxN&P)2tfjPNKj&(97X(63zAuD-$c!~z=n76((77Cj|Tjzl9RTNxWqpi7=N?ommi2RDS=Cc;Ji}F?>EDo?X1+wPG&S!L^lih%byG|1 z!{rVPKIw5IUXvWM?ddeUpF?j9`tP87wVqYuoU=!WZpkmYg@Z#Flly&Zzr>$s?)z?W z2mk;832;bRa{vGf5&!@T5&_cPe*6Fc0f0$FK~!i%?ONRpf?y0p;|q&8k83!QYd8;! z_^81|qXy{pYL&wIN`J1s@9R-w)9APC4s0^ZYftAZ6aR8Nq~Y2zfIL2z{oZ5Vv|Z=2 z_Zn}#4%O{daCHVP{9@SZQ1$1~_-Ey%Z_cl$v?Xb%Z zEKYc64JAN^1Pd(7FI4bdb`m$6jG}diGrBsbm|}QNY-C^&!x>$jQ_QC_YP)1VIr*?CgCtBIj%(L?qc`Za#jyzhQP3ywRM>J5A$|FO=-qRK!?_FB;7+ zwBncO#g|ZyH8C?2rQ!O#!=v-`m zGM7`9^Q^8(u#RQLKO(|tp+z)%o^d(#xTk#9B$RIUXw@RIwHJGXaj2z{b^ZQ&F5CQm zy%WasrSdB6O0000') +} +.md-typeset .admonition.link, +.md-typeset details.link { + border-color: rgb(68, 138, 255); +} +.md-typeset .link > .admonition-title, +.md-typeset .link > summary { + background-color: rgba(68, 138, 255, 0.1); +} +.md-typeset .link > .admonition-title::before, +.md-typeset .link > summary::before { + background-color: rgb(68, 138, 255); + + mask-image: var(--md-admonition-icon--link); + -webkit-mask-image: var(--md-admonition-icon--link); +} \ No newline at end of file diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml new file mode 100644 index 0000000000..f5072ad375 --- /dev/null +++ b/docs/mkdocs.yml @@ -0,0 +1,80 @@ +site_name: GregTech Modern Docs +site_url: https://gregtechceu.github.io/GregTech-Modern + +repo_name: GregTech Modern +repo_url: https://github.com/GregTechCEu/GregTech-Modern +edit_uri: blob/1.20.1/docs/content/ + +docs_dir: content + +theme: + name: material + logo: assets/logo.png + favicon: assets/favicon.png + icon: + repo: fontawesome/brands/github + palette: + # Palette toggle for light mode + - scheme: default + primary: custom + accent: custom + toggle: + icon: material/brightness-7 + name: Switch to dark mode + # Palette toggle for dark mode + - scheme: slate + primary: custom + accent: custom + toggle: + icon: material/brightness-4 + name: Switch to light mode + features: + - navigation.instant + - navigation.sections + - navigation.indexes + - navigation.tabs + - navigation.tabs.sticky + - navigation.top + - toc.follow + - content.code.annotate + - content.action.view + +plugins: + - search + - awesome-pages + +extra_css: + - stylesheets/extra.css + +extra: + social: + - icon: fontawesome/brands/discord + link: https://discord.gg/bWSWuYvURP + - icon: simple/curseforge + link: https://www.curseforge.com/minecraft/mc-mods/gregtechceu-modern/files + - icon: simple/modrinth + link: https://modrinth.com/mod/gregtechceu-modern + - icon: fontawesome/brands/github + link: https://github.com/GregTechCEu/GregTech-Modern + +markdown_extensions: + - admonition + - attr_list + - def_list + - tables + - md_in_html + - pymdownx.betterem + - pymdownx.caret: + insert: false + - pymdownx.tilde: + delete: false + - pymdownx.superfences + - pymdownx.details + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.inlinehilite + - pymdownx.tabbed: + alternate_style: true + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg diff --git a/docs/requirements.txt b/docs/requirements.txt new file mode 100644 index 0000000000..80c8e8446b --- /dev/null +++ b/docs/requirements.txt @@ -0,0 +1,2 @@ +mkdocs-material +mkdocs-awesome-pages-plugin