├── .github ├── ISSUE_TEMPLATE │ ├── blank.yml │ ├── bug_report.yml │ ├── config.yml │ └── enhancement.yml └── workflows │ ├── mdbook.yml │ └── sync_has_pull_request_label.yml ├── .gitignore ├── LICENSE ├── README.md ├── book.toml └── src ├── SUMMARY.md ├── contributor-guide.md ├── images └── compound_entities_1.png └── internal_apis ├── README.md ├── compound_entities.md ├── pyalienlife ├── README.md ├── digosaurs.md └── turd.md ├── pypostprocessing ├── data_stage.md ├── entity.md ├── fluid.md ├── item.md ├── recipe.md ├── technology.md ├── template.md └── tile.md └── pystellarexpedition ├── README.md └── rocket-modules.md /.github/ISSUE_TEMPLATE/blank.yml: -------------------------------------------------------------------------------- 1 | name: "Blank Issue" 2 | description: "Start with a completely blank issue template" 3 | labels: [] 4 | body: 5 | - type: textarea 6 | id: freeform 7 | attributes: 8 | label: Describe your issue 9 | description: Feel free to write anything here — no structure, just your thoughts. 10 | placeholder: Write your issue here... 11 | validations: 12 | required: false 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report to help improve Pyanodon mods. 3 | labels: ["triage"] 4 | body: 5 | - type: markdown 6 | attributes: 7 | value: | 8 | #### PLEASE NOTE 9 | We do not support isssues related to running versions from GitHub mixed with versions from the Factorio mod portal. 10 | 11 | Please post all details in **English**. 12 | 13 | #### Prerequisites before submitting an issue! 14 | - Check the **[frequent/common issues list](https://github.com/pyanodon/pybugreports/issues/1)** and perform a **[search of submitted issues (including closed ones)](https://github.com/search?q=user%3Apyanodon&type=issues)** to avoid posting a duplicate. 15 | - Make sure this is not a support request or question, both of which are better suited for either the **[discussions section](https://github.com/pyanodon/pybugreports/discussions)** or **[discord](https://discord.gg/PtDdRtYZrx)**. 16 | - Verify that you can reproduce the issue with the **absolute minimum set of mods** (including any required to cause the issue, but no more). 17 | - Do you have programming experience? Consider instead submitting a [Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request) to help reduce our workload. 18 | - **Always** include the factorio-current.log file. Additionally, if relevant to issue/when asked, please provide a savegame & screenshots. 19 | - type: dropdown 20 | attributes: 21 | label: Mod source 22 | description: Where did you acquire the py mods you are having issues with? 23 | multiple: false 24 | options: 25 | - Factorio Mod Portal 26 | - Github 27 | - Mixed GitHub and Factorio Mod Portal 28 | - PyAE Alpha 29 | - PyAE Beta 30 | - Other 31 | validations: 32 | required: true 33 | - type: dropdown 34 | attributes: 35 | label: Operating system 36 | description: Which operating system are you running the game on? 37 | multiple: false 38 | options: 39 | - ">=Windows 10" 40 | - ">=Windows 10 w/ case sensitive filesystem enabled" 41 | - < Windows 10 42 | - MacOS 43 | - GNU/Linux 44 | validations: 45 | required: true 46 | - type: checkboxes 47 | id: issuetype 48 | attributes: 49 | label: What kind of issue is this? 50 | description: You may select more than one. 51 | options: 52 | - label: Compatibility 53 | - label: Locale (names, descriptions, unknown keys) 54 | - label: Graphical 55 | - label: Crash 56 | - label: Progression 57 | - label: Balance 58 | - label: Pypostprocessing failure 59 | - label: Other 60 | - type: textarea 61 | attributes: 62 | label: What is the problem? 63 | description: Please give a clear and concise description of the problem. 64 | validations: 65 | required: true 66 | - type: textarea 67 | attributes: 68 | label: Steps to reproduce 69 | description: Please provide reliable steps to reproduce the problem. 70 | placeholder: | 71 | 1. First step 72 | 2. Second step 73 | 3. and so on... 74 | validations: 75 | required: false 76 | - type: textarea 77 | attributes: 78 | label: Additional context 79 | description: Add screenshots etc. (Anything that will provide more context about the problem) 80 | validations: 81 | required: false 82 | - type: textarea 83 | attributes: 84 | label: Log file 85 | description: | 86 | Add this file: factorio-current.log. It can be found at %APPDATA%\Factorio\factorio-current.log on most Windows installations, or in the Factorio game folder. 87 | validations: 88 | required: false 89 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Question 4 | url: "https://github.com/pyanodon/pybugreports/discussions" 5 | about: "Unsure if it's an issue, or just have a question? Ask in discussions first." 6 | 7 | - name: Question 8 | url: "https://discord.gg/PtDdRtYZrx" 9 | about: "Alternatively, ask on our discord" 10 | 11 | - name: "Blank Issue" 12 | path: blank.yml 13 | description: "Start with a completely blank issue template" 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/enhancement.yml: -------------------------------------------------------------------------------- 1 | name: "Enhancement" 2 | description: "Define an enhancement, explain why, and outline steps to achieve it" 3 | labels: ["enhancement"] 4 | body: 5 | - type: textarea 6 | id: goal 7 | attributes: 8 | label: "Goal" 9 | description: "What do you want to accomplish?" 10 | placeholder: "e.g. Add a system to automatically notify users of updates" 11 | validations: 12 | required: true 13 | 14 | - type: textarea 15 | id: why 16 | attributes: 17 | label: "Why?" 18 | description: "Why is this goal important or needed?" 19 | placeholder: "e.g. Keeps users informed without manual updates" 20 | validations: 21 | required: true 22 | 23 | - type: textarea 24 | id: steps 25 | attributes: 26 | label: "Steps to achieve? (if known)" 27 | description: "Optional: Outline steps or ideas for how to accomplish the goal" 28 | placeholder: "- Step 1\n- Step 2\n- Step 3" 29 | validations: 30 | required: false 31 | 32 | -------------------------------------------------------------------------------- /.github/workflows/mdbook.yml: -------------------------------------------------------------------------------- 1 | # Sample workflow for building and deploying a mdBook site to GitHub Pages 2 | # 3 | # To get started with mdBook see: https://rust-lang.github.io/mdBook/index.html 4 | # 5 | name: Deploy mdBook site to Pages 6 | 7 | on: 8 | # Runs on pushes targeting the default branch 9 | push: 10 | branches: ["main"] 11 | 12 | # Allows you to run this workflow manually from the Actions tab 13 | workflow_dispatch: 14 | 15 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 16 | permissions: 17 | contents: read 18 | pages: write 19 | id-token: write 20 | 21 | # Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. 22 | # However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. 23 | concurrency: 24 | group: "pages" 25 | cancel-in-progress: false 26 | 27 | jobs: 28 | # Build job 29 | build: 30 | runs-on: ubuntu-latest 31 | env: 32 | MDBOOK_VERSION: 0.4.36 33 | steps: 34 | - uses: actions/checkout@v4 35 | - name: Install mdBook 36 | run: | 37 | curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf -y | sh 38 | rustup update 39 | cargo install --version ${MDBOOK_VERSION} mdbook 40 | - name: Setup Pages 41 | id: pages 42 | uses: actions/configure-pages@v5 43 | - name: Build with mdBook 44 | run: mdbook build 45 | - name: Upload artifact 46 | uses: actions/upload-pages-artifact@v3 47 | with: 48 | path: ./book 49 | 50 | # Deployment job 51 | deploy: 52 | environment: 53 | name: github-pages 54 | url: ${{ steps.deployment.outputs.page_url }} 55 | runs-on: ubuntu-latest 56 | needs: build 57 | steps: 58 | - name: Deploy to GitHub Pages 59 | id: deployment 60 | uses: actions/deploy-pages@v4 61 | -------------------------------------------------------------------------------- /.github/workflows/sync_has_pull_request_label.yml: -------------------------------------------------------------------------------- 1 | name: Sync "has pull request" label 2 | 3 | on: 4 | schedule: 5 | - cron: '0 * * * *' 6 | workflow_dispatch: 7 | 8 | permissions: 9 | contents: read 10 | issues: write 11 | 12 | jobs: 13 | sync: 14 | runs-on: ubuntu-latest 15 | env: 16 | LABEL: has pull request 17 | 18 | steps: 19 | - name: Run label sync 20 | uses: actions/github-script@v7 21 | with: 22 | github-token: ${{ secrets.CROSS_REPO_PAT || github.token }} 23 | script: | 24 | const { owner, repo } = context.repo; 25 | const labelName = process.env.LABEL; 26 | 27 | console.log(`🔍 Starting sync for ${owner}/${repo}`); 28 | console.log(`Label to apply: "${labelName}"`); 29 | 30 | const issues = await github.paginate( 31 | github.rest.issues.listForRepo, 32 | { owner, repo, state: "open", per_page: 100 } 33 | ); 34 | 35 | console.log(`📦 Found ${issues.length} open issues.`); 36 | 37 | for (const issue of issues) { 38 | if (issue.pull_request) { 39 | console.log(`⏭️ Skipping pull request #${issue.number}`); 40 | continue; 41 | } 42 | 43 | const issueNumber = issue.number; 44 | const issueURL = issue.html_url; 45 | const hasLabel = issue.labels.some(l => l.name === labelName); 46 | 47 | console.log(`\n🔎 Checking issue #${issueNumber}: ${issue.title}`); 48 | console.log(`↳ URL: ${issueURL}`); 49 | console.log(`↳ Labels: ${issue.labels.map(l => l.name).join(", ") || "none"}`); 50 | 51 | const timeline = await github.paginate( 52 | github.rest.issues.listEventsForTimeline, 53 | { 54 | owner, 55 | repo, 56 | issue_number: issueNumber, 57 | per_page: 100, 58 | mediaType: { 59 | previews: ["mockingbird"] 60 | } 61 | } 62 | ); 63 | 64 | console.log(`🧾 Timeline has ${timeline.length} events.`); 65 | timeline.forEach((event, i) => { 66 | const type = event.source?.type || "none"; 67 | const prURL = event.source?.issue?.html_url || "—"; 68 | console.log(` - ${i + 1}. event="${event.event}" type=${type} url=${prURL}`); 69 | }); 70 | 71 | const linkedOpenPRs = []; 72 | 73 | for (const event of timeline) { 74 | const pr = event.source?.issue; 75 | const prURL = pr?.html_url || "(unknown)"; 76 | const state = pr?.state || "(missing)"; 77 | const draft = pr?.draft ?? "(unknown)"; 78 | const isLikelyPR = prURL.includes("/pull/"); 79 | 80 | if (event.event === "cross-referenced" && isLikelyPR) { 81 | console.log(`🔗 Cross-ref PR: ${prURL} (state=${state}, draft=${draft})`); 82 | if (state === "open" && draft === false) { 83 | linkedOpenPRs.push(pr); 84 | } 85 | } 86 | } 87 | 88 | if (linkedOpenPRs.length > 0 && !hasLabel) { 89 | console.log(`✅ Adding "${labelName}" to issue #${issueNumber}`); 90 | await github.rest.issues.addLabels({ 91 | owner, 92 | repo, 93 | issue_number: issueNumber, 94 | labels: [labelName] 95 | }); 96 | } else if (linkedOpenPRs.length === 0 && hasLabel) { 97 | console.log(`🗑️ Removing "${labelName}" from issue #${issueNumber}`); 98 | try { 99 | await github.rest.issues.removeLabel({ 100 | owner, 101 | repo, 102 | issue_number: issueNumber, 103 | name: labelName 104 | }); 105 | } catch (err) { 106 | if (err.status === 404) { 107 | console.log(`⚠️ Label already gone from #${issueNumber}`); 108 | } else { 109 | throw err; 110 | } 111 | } 112 | } else { 113 | console.log(`🔸 No label change needed for issue #${issueNumber}`); 114 | } 115 | } 116 | 117 | console.log(`🎉 Sync complete.`); 118 | 119 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | book/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pyanodon Mods Bug Reports 2 | This is the repository for bug reports and other feedback about the Pyanodon mods. 3 | * To report a bug, please navigate to the [Issues](https://github.com/pyanodon/pybugreports/issues) section. 4 | * If it's not a bug, then please consider using [Discussions](https://github.com/pyanodon/pybugreports/discussions) instead. The Discussions section is fitting for questions, issues that you're not sure about, suggestions, or any other feedback. 5 | -------------------------------------------------------------------------------- /book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["lambdalemon"] 3 | language = "en" 4 | src = "src" 5 | title = "pydocs" 6 | -------------------------------------------------------------------------------- /src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | - [Contributor Guide](./contributor-guide.md) 4 | - [Internal APIs](./internal_apis/README.md) 5 | - [PyPostProcessing Data Stage](./internal_apis/pypostprocessing/data_stage.md) 6 | - [Item](./internal_apis/pypostprocessing/item.md) 7 | - [Entity](./internal_apis/pypostprocessing/entity.md) 8 | - [Fluid](./internal_apis/pypostprocessing/fluid.md) 9 | - [Recipe](./internal_apis/pypostprocessing/recipe.md) 10 | - [Technology](./internal_apis/pypostprocessing/technology.md) 11 | - [Tile](./internal_apis/pypostprocessing/tile.md) 12 | - [Compound Entities](./internal_apis/compound_entities.md) 13 | - [PyStellarExpedition](./internal_apis/pystellarexpedition/README.md) 14 | - [Rocket Modules](./internal_apis/pystellarexpedition/rocket-modules.md) 15 | - [PyAlienLife](./internal_apis/pyalienlife/README.md) 16 | - [Digosaurs](./internal_apis/pyalienlife/digosaurs.md) 17 | - [T.U.R.D.](./internal_apis/pyalienlife/turd.md) 18 | -------------------------------------------------------------------------------- /src/contributor-guide.md: -------------------------------------------------------------------------------- 1 | # Contributor Guide 2 | *As of August 16th, 2025*
3 | 4 | ### How to commit 5 | 6 | List of good resources to find out what to do: 7 | - [Good first Issues](https://github.com/pyanodon/pybugreports/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22good%20first%20issue%22) 8 | - [Py Roadmap](https://github.com/orgs/pyanodon/projects/2/views/4) 9 | 10 | How can I contribute? 11 | 1. Clone the repo you want to commit to 12 | 2. Create a new branch off of main 13 | 3. Create the changes you want to add 14 | 4. Create a pull request describing what you fixed or what you're adding, for simple bug reports you can just say “Fixes ” 15 | 5. Wait for approval 16 | 6. Make changes if needed 17 | 7. Boom dopamine 18 | 19 | ### The pY Contributor Feng Shui 20 | 21 | You should aim to make your code reusable, extensible. 22 | 23 | Docs are located [here](https://pyanodon.github.io/pybugreports/) 24 | 25 | If you're adding new functions make sure to document them using [luadoc format](https://stevedonovan.github.io/ldoc/manual/doc.md.html). You should also go into `pybugreports` and edit the `src` folder files and add a new page or edit an existing page to add your new contributions. While editting / creating this new file ensure to change the date at the top saying `As of September 32 1685` to the current date as of editting, do not worry about timezones. Try and make your new contributions to the docs fit the style as the content in the file. 26 | 27 | ### General Layout 28 | 29 | We have 2 branches for different updates: Master or Main and Breaking Changes 30 | - Main or Master is used for bug fixes, general fixes, or non-removing enhancements. 31 | - Breaking changes is used for breaking changes like changing the output of a recipe to have a new byproduct. 32 | 33 | The codebase will look something like this typically 34 | ``` 35 | pytesting 36 | ├── control.lua 37 | ├── data.lua 38 | ├── scripts 39 | │ └── example.lua 40 | └── prototypes 41 | ├── buildings 42 | │ └── example-building.lua 43 | ├── items 44 | │ └── items.lua 45 | ├── recipes 46 | │ └── recipes.lua 47 | └── technologies 48 | └── exmple-technology.lua 49 | ``` 50 | 51 | ### Helpful scripts 52 | 53 | #### Platform Agnostic 54 | 55 | Clones all of the py repos 56 | ``` 57 | git clone https://github.com/pyanodon/pyalienlife.git 58 | git clone https://github.com/pyanodon/pypostprocessing.git 59 | git clone https://github.com/pyanodon/pycoalprocessing.git 60 | git clone https://github.com/pyanodon/pyfusionenergy.git 61 | git clone https://github.com/pyanodon/pyhightech.git 62 | git clone https://github.com/pyanodon/pyindustry.git 63 | git clone https://github.com/pyanodon/pyrawores.git 64 | git clone https://github.com/pyanodon/pypetroleumhandling.git 65 | git clone https://github.com/pyanodon/pyalternativeenergy.git 66 | ``` 67 | 68 | #### Linux 69 | 70 | Simple script to reset all branches to the main/master branch and pull in a directory: 71 | ```sh 72 | cd "$(dirname "$0")" 73 | 74 | for folder in py*/; do 75 | if [ -d "$folder" ]; then 76 | cd "$folder" || continue 77 | 78 | # Get the default remote branch (e.g., origin/main or origin/master) 79 | default_branch=$(git symbolic-ref refs/remotes/origin/HEAD --short | sed 's|origin/||') 80 | 81 | git checkout "$default_branch" 82 | git pull --rebase 83 | 84 | cd .. 85 | fi 86 | done 87 | ``` 88 | 89 | Updates everything from the current branch it's on: 90 | ```sh 91 | # Exit on error 92 | set -e 93 | 94 | # Loop through each directory starting with "py" that is a git repo 95 | for dir in py*/; do 96 | if [ -d "$dir/.git" ]; then 97 | echo "Processing repository: $dir" 98 | cd "$dir" 99 | 100 | # Pull the latest changes from the remote 101 | echo "Updating repository..." 102 | git pull 103 | 104 | # If the ignore revs file exists, configure blame to use it 105 | if [ -f ".git-blame-ignore-revs" ]; then 106 | echo "Configuring git to ignore revs in .git-blame-ignore-revs" 107 | git config blame.ignoreRevsFile .git-blame-ignore-revs 108 | else 109 | echo "No .git-blame-ignore-revs file found in $dir" 110 | fi 111 | 112 | cd .. 113 | fi 114 | done 115 | 116 | echo "All repositories processed." 117 | ``` 118 | 119 | Lists all git branches: 120 | ```sh 121 | find . -type d -name ".git" | while read -r gitdir; do 122 | repo=$(dirname "$gitdir") 123 | branch=$(git -C "$repo" rev-parse --abbrev-ref HEAD 2>/dev/null) 124 | echo "$repo: $branch" 125 | done 126 | ``` 127 | 128 | 129 | ### Frequently Asked Questions 130 | 131 | Where are pypostprocessing functions located at? 132 | 133 | `pypostprocessing/lib` for in-general stuff
134 | `pypostprocessing/lib/metas` for prototyping functions
135 | `pypostprocessing/lib/events` for control event functions 136 | -------------------------------------------------------------------------------- /src/images/compound_entities_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyanodon/pybugreports/8c9008be6072b52ae5ce2440c54b08ed134baf8d/src/images/compound_entities_1.png -------------------------------------------------------------------------------- /src/internal_apis/README.md: -------------------------------------------------------------------------------- 1 | # Internal APIs 2 | 3 | Internal APIs are things like `pypostprocessing` functions. 4 | -------------------------------------------------------------------------------- /src/internal_apis/compound_entities.md: -------------------------------------------------------------------------------- 1 | # Compound Entities 2 | *As of August 21st, 2025*
3 | 4 | Compound entities are entities made up of multiple entities. For example vatbrains are just assembling-machines with a beacon hidden inside them. 5 | 6 | These are typically a multi-step process of tracking these manually with storage variable and when things have been placed down and integrating GUI. This is to be put simply, annoying. So I (Lemon) did what any sane programmer would do and just made a special API for it. 7 | 8 | ## How to use? 9 | 10 | The meat and potatoes of this api is that you can do this very easily just at data stage. 11 | 12 | Here's a minimal example of how 13 | ```lua 14 | local parent = "assembling-machine" 15 | local child = "beacon" 16 | py.compound_attach_entity_to(parent, child, {}) 17 | ``` 18 | And then in the control stage 19 | ```lua 20 | require "__pypostprocessing__.lib" -- If you don't have this somewhere already 21 | 22 | -- This should be after every other compound entity function as well 23 | py.register_compound_entities() 24 | py.finalize_events() -- If you don't have this either already 25 | ``` 26 | That's it, you have an assembling machine with a beacon now. 27 | 28 | Wait but what about that empty table at the end? 29 | 30 | ### Example 31 | 32 | ```lua 33 | py.compound_attach_entity_to("jaw-crusher", "beacon", { 34 | enable_gui = true 35 | }) 36 | ``` 37 | This is what this looks like: 38 | ![Image](../images/compound_entities_1.png) 39 | 40 | # API Specification 41 | How this works internally is that compound entity attachments get attached in data stage then smuggled using `mod_data` over to control stage. 42 | 43 | In control stage we then register a few events to detect when things are placed or picked up and other functions. 44 | 45 | ## Data 46 | ```lua 47 | -- Attachs an entity to another entity with additional properties 48 | -- @param parent string 49 | -- @param child string 50 | -- 51 | -- @param additional AdditionalParams 52 | -- @class AdditionalParams 53 | -- @field enable_gui bool Enables an entry in the gui of the parent 54 | -- @field gui_title string The title of the parent gui 55 | -- @field gui_function_name string The name of the register compound function that handles adding the button to the gui 56 | -- @field gui_submenu_function string The fuction called when you hit the button itself 57 | -- @field gui_caption string The text the button has 58 | -- @field position_offset MapPosition https://lua-api.factorio.com/2.0.64/concepts/MapPosition.html 59 | -- @field on_built string Name of compound_entity function to run when the entity is built 60 | -- 61 | -- @see https://pyanodon.github.io/pybugreports/internal_apis/compound_entities.html 62 | function py.compound_attach_entity_to(parent, child, additional) 63 | -- ... 64 | end 65 | ``` 66 | 67 | ## Control 68 | ### Register Compound Function 69 | ```lua 70 | -- Registers a new compound function 71 | -- @param name string Name of the function 72 | -- @param func (GuiTitleFunction|GuiFunction|GuiSubmenuFunction|OnChildBuiltFunction) 73 | -- 74 | -- @function GuiTitleFunction 75 | -- @param entity LuaEntity parent entity 76 | -- @return string Title 77 | -- 78 | -- @function GuiFunction 79 | -- @param event events.on_gui_opened Event data of when on_gui_opened is called 80 | -- @param player LuaEntity the player who opened the GUI 81 | -- @param gui_root LuaGuiElement The root of the preset GUI that you can add to 82 | -- @param current_index number The index of the compound-entity child you are 83 | -- @param gui_child LuaGuiElement The Button you are in the the gui_root 84 | -- @return nil 85 | -- 86 | -- @function GuiSubmenuFunction 87 | -- @param entity Parent entity 88 | -- @return (LuaGuiElement|LuaEntity) Anything that can be put in `player.opened` 89 | -- 90 | -- @function OnChildBuiltFunction 91 | -- @param child LuaEntity the child entity 92 | -- @return nil 93 | -- 94 | -- @see https://pyanodon.github.io/pybugreports/internal_apis/compound_entities.html 95 | function py.register_compound_function(name, func) 96 | -- ... 97 | end 98 | ``` 99 | 100 | ### Get Compound Function Name 101 | ```lua 102 | -- Gets a registered compound_function from a name 103 | -- @param name string The name of the function 104 | function py.get_compound_function(name) 105 | -- ... 106 | end 107 | ``` 108 | 109 | ### Get Compound Entity Children 110 | ```lua 111 | -- Gets the compound entity's children from it's unit_number 112 | -- @param unit_number number Unit number of the parent 113 | function py.get_compound_entity_children(unit_number) 114 | -- ... 115 | end 116 | ``` 117 | 118 | ### Get Compound Entity Parent 119 | ```lua 120 | -- Gets the compound_entity parent from a child's unit_number 121 | -- @param unit_number number Unit number of a child 122 | function py.get_compound_entity_parent(unit_number) 123 | -- ... 124 | end 125 | ``` 126 | 127 | ### Register Compound Entities 128 | ```lua 129 | -- Register all compound_entities and create their events 130 | function py.register_compound_entities() 131 | -- ... 132 | end 133 | ``` 134 | 135 | #### Compound Attach Entity To 136 | ```lua 137 | -- Adds a new child to a compound entity 138 | -- Unsure about using this on non compound register parents, beware... 139 | -- 140 | -- @param parent LuaEntity the parent compound entity 141 | -- @param child string Name of the child 142 | -- @param info Info 143 | -- 144 | -- @class Info 145 | -- @field enable_gui bool Not sure if it works but it's the same as the normal enable_gui property 146 | -- @field possition_offset MapPosition https://lua-api.factorio.com/2.0.64/concepts/MapPosition.html 147 | -- @field on_built string Name of compound_entity function to run when the entity is built 148 | function py.compound_attach_entity_to(parent, child_name, info) 149 | -- ... 150 | end 151 | ``` 152 | 153 | #### Delete Attached Entity by Filter Function 154 | ```lua 155 | -- Deletes children from a parent by a filter function 156 | -- Do not worry about validity it is checked internally 157 | -- 158 | -- @param parent_unit_number number Unit number of the parent 159 | -- @param filter_func fun(child: LuaEntity): bool Return true if delete 160 | function py.delete_attached_entities_by_filter(parent_unit_number, filter_func) 161 | ``` 162 | -------------------------------------------------------------------------------- /src/internal_apis/pyalienlife/README.md: -------------------------------------------------------------------------------- 1 | # PyAlienLife 2 | 3 | Some APIs declared and used in `pyalienlife`. 4 | -------------------------------------------------------------------------------- /src/internal_apis/pyalienlife/digosaurs.md: -------------------------------------------------------------------------------- 1 | # Digosaurs 2 | *As of August 16th, 2025*
3 | 4 | You can add a new digosaur like this: 5 | ```lua 6 | local name = "my-new-digosaur" 7 | local bonus = 2 8 | local proxy_name = "my-nex-digosaur-proxy" 9 | remote.call("py_digosaurs", "new_digosaur", name, bonus, proxy_name) 10 | ``` 11 | 12 | You can also remove existing digosaurs like so: 13 | ```lua 14 | remote.call("py_digosaurs", "remove_digosaur", "digosaurus") 15 | ``` 16 | -------------------------------------------------------------------------------- /src/internal_apis/pyalienlife/turd.md: -------------------------------------------------------------------------------- 1 | # T.U.R.D. 2 | *As of August 17th, 2025* 3 | 4 | ```lua 5 | remote.add_interface("pywiki_turd_page", { 6 | create_turd_page = create_turd_page, 7 | on_search = on_search, 8 | reapply_turd_bonuses = reapply_turd_bonuses, 9 | new_turd = new_turd, 10 | on_turd_built = on_turd_built 11 | get_machine_replacement = get_machine_replacement 12 | }) 13 | ``` 14 | 15 | ## New TURD 16 | This function is the function called when clicking on the TURD select button. 17 | 18 | It can be used like this to select an arbitrary TURD, if a TURD is already selected it will deselect it. 19 | 20 | ```lua 21 | local fake_event = { 22 | skip_gui = true, -- Need this or it will error out because of gui events 23 | player = game.player, 24 | master_tech_name = master_tech_name, -- The technology that unlocks the turd 25 | sub_tech_name = sub_tech_name, -- The turd name 26 | } 27 | 28 | remote.call("pywiki_turd_page", "new_turd", fake_event) 29 | end 30 | ``` 31 | 32 | ## get_machine_replacement 33 | This function returns any applied machine replacement turds for the given entity 34 | 35 | ```lua 36 | ---@param force_index integer the force requesting this information 37 | ---@param entity_name string the entity get the replacement for 38 | ---@return string? replacement_entity the name of the entity that replaces the given entity 39 | ``` 40 | 41 | ## List of all TURD Techs and Subtechs 42 | 43 | ``` 44 | arqad-upgrade = { 45 | air-conditioner, 46 | cags, 47 | drone, 48 | }, 49 | arthurian-upgrade = { 50 | abacus, 51 | heated-stone, 52 | cannibalism, 53 | }, 54 | atomizer-upgrade = { 55 | sc-core, 56 | sub-atomic, 57 | d-core, 58 | }, 59 | auog-upgrade = { 60 | sawdust, 61 | glowing-mushrooms, 62 | underground-chambers, 63 | }, 64 | bhoddos-upgrade = { 65 | extra-drones, 66 | exoenzymes, 67 | gills, 68 | }, 69 | biofactory-upgrade = { 70 | molecular-polyentomology, 71 | compusun, 72 | resonant, 73 | }, 74 | bioprinting-upgrade = { 75 | high-viability, 76 | biomimetics, 77 | covalent, 78 | }, 79 | bioreactor-upgrade = { 80 | aerators, 81 | baffles, 82 | jacket, 83 | }, 84 | cadaveric-arum-upgrade = { 85 | acid-comtemplator, 86 | solar-scope, 87 | e-photo, 88 | }, 89 | compost-upgrade = { 90 | constant, 91 | humus, 92 | worm-hotel, 93 | }, 94 | cottongut-upgrade = { 95 | igm, 96 | ts, 97 | ud, 98 | }, 99 | creature-chamber-upgrade = { 100 | respiratory, 101 | neural-fusion, 102 | cc, 103 | }, 104 | cridren-upgrade = { 105 | sixth-layer, 106 | neural-cranio, 107 | mufflers, 108 | }, 109 | data-array-upgrade = { 110 | booster, 111 | dbwt, 112 | solar-p, 113 | }, 114 | dhilmos-upgrade = { 115 | cover, 116 | skimmer, 117 | double-intake, 118 | }, 119 | dingrits-upgrade = { 120 | alpha, 121 | c-mutation, 122 | training, 123 | }, 124 | fast-wood-forestry-upgrade = { 125 | dry-storage, 126 | selective-heads, 127 | self-generation, 128 | }, 129 | fawogae-upgrade = { 130 | n2-ferti, 131 | acidosis, 132 | dry, 133 | }, 134 | fish-upgrade = { 135 | a-select, 136 | temp-control, 137 | dosing-pump, 138 | }, 139 | genlab-upgrade = { 140 | hsn, 141 | enn, 142 | dwx, 143 | }, 144 | grod-upgrade = { 145 | hi-sprinkler, 146 | ground-irrigation, 147 | carbide-c, 148 | }, 149 | guar-upgrade = { 150 | guarpulse, 151 | aquaguar, 152 | hh, 153 | }, 154 | incubator-upgrade = { 155 | gs, 156 | zero, 157 | icd, 158 | }, 159 | kicalk-upgrade = { 160 | wire-netting, 161 | extra-water, 162 | crop-rotation, 163 | }, 164 | kmauts-upgrade = { 165 | sex-ratio, 166 | eye-out, 167 | moult-recycle, 168 | }, 169 | korlex-upgrade = { 170 | multi-tit, 171 | high-pressure, 172 | nx-heat-pump, 173 | }, 174 | moondrop-upgrade = { 175 | cu, 176 | moon, 177 | carbon-capture, 178 | }, 179 | moss-upgrade = { 180 | spores, 181 | hd-moss, 182 | inbuilt-moss, 183 | remove-muddy-sludge, 184 | }, 185 | mukmoux-upgrade = { 186 | zero-cross, 187 | bip, 188 | think-collar, 189 | }, 190 | navens-upgrade = { 191 | cytotoxicity, 192 | pre-sterilization, 193 | lichen, 194 | }, 195 | numal-upgrade = { 196 | d2o, 197 | nc, 198 | neutron-bombardment, 199 | }, 200 | phadai-upgrade = { 201 | ethanol-boost, 202 | piezoelectric-floor, 203 | dubstep-track, 204 | }, 205 | phagnot-upgrade = { 206 | leader, 207 | socialization, 208 | hr, 209 | }, 210 | ralesia-upgrade = { 211 | improved-burst, 212 | sun-concentration, 213 | h2-recycle, 214 | }, 215 | rennea-upgrade = { 216 | deadheading, 217 | alltime, 218 | aphid-cleaning, 219 | }, 220 | research-upgrade = { 221 | unstable, 222 | ms, 223 | spg, 224 | mci, 225 | }, 226 | sap-upgrade = { 227 | inoculator, 228 | patch, 229 | bark, 230 | }, 231 | schrodinger-antelope-upgrade = { 232 | pentadimensional, 233 | existential-hazard, 234 | higgs-field, 235 | }, 236 | scrondrix-upgrade = { 237 | boronb, 238 | hspa, 239 | neuron, 240 | }, 241 | seaweed-upgrade = { 242 | improved-pathfinding, 243 | precise-cutting, 244 | recirculation-pump, 245 | }, 246 | simik-digestion-mk01 = { 247 | simik-iron, 248 | simik-copper, 249 | simik-quartz, 250 | }, 251 | simik-digestion-mk02 = { 252 | simik-coal, 253 | simik-tin, 254 | simik-aluminium, 255 | }, 256 | simik-digestion-mk03 = { 257 | simik-boron, 258 | simik-chromium, 259 | simik-molybdenum, 260 | }, 261 | simik-digestion-mk04 = { 262 | simik-zinc, 263 | simik-nickel, 264 | simik-lead, 265 | }, 266 | simik-digestion-mk05 = { 267 | simik-titanium, 268 | simik-niobium, 269 | simik-nexelit, 270 | }, 271 | simik-digestion-mk06 = { 272 | simik-silver, 273 | simik-gold, 274 | simik-uranium, 275 | }, 276 | slaughterhouse-upgrade = { 277 | laser-cutting, 278 | mercy-killing, 279 | lard-machine, 280 | }, 281 | sponge-upgrade = { 282 | flagellum, 283 | fragmentation, 284 | bacterial, 285 | }, 286 | trits-upgrade = { 287 | mgo, 288 | dc, 289 | nexelit-axis, 290 | }, 291 | tuuphra-upgrade = { 292 | fi, 293 | fungicide, 294 | tr, 295 | }, 296 | ulric-upgrade = { 297 | dummy-ulric, 298 | heated-pads, 299 | scraping-bots, 300 | }, 301 | vonix-upgrade = { 302 | evoa, 303 | uge, 304 | dermal, 305 | }, 306 | vrauks-upgrade = { 307 | reuse-water, 308 | natural-cycle, 309 | cyanic-recycling, 310 | }, 311 | wood-processing-unit-upgrade = { 312 | biosynthetic-nylon, 313 | sawblades, 314 | carbonefarious, 315 | }, 316 | xeno-upgrade = { 317 | ap, 318 | herm, 319 | hive, 320 | }, 321 | xyhiphoe-upgrade = { 322 | temp-c, 323 | rst, 324 | reuse-ev, 325 | }, 326 | yaedols-upgrade = { 327 | sub-s, 328 | duct, 329 | humidity-control, 330 | }, 331 | yotoi-upgrade = { 332 | cryopreservation, 333 | harvest, 334 | nutrinet, 335 | }, 336 | zipir-upgrade = { 337 | suicide, 338 | sr, 339 | hatchery, 340 | }, 341 | zungror-upgrade = { 342 | geooxidation, 343 | genooscillation, 344 | oviduct-bombardment, 345 | } 346 | ``` 347 | -------------------------------------------------------------------------------- /src/internal_apis/pypostprocessing/data_stage.md: -------------------------------------------------------------------------------- 1 | # PyPostProcessing Data Stage 2 | 3 | General functions used in Data Stage created by `pypostprocessing`. 4 | -------------------------------------------------------------------------------- /src/internal_apis/pypostprocessing/entity.md: -------------------------------------------------------------------------------- 1 | # Entity 2 | *As of August 16th, 2025*
3 | ```lua 4 | ---@class data.EntityPrototype 5 | ---@field public standardize fun(self: data.EntityPrototype): data.EntityPrototype 6 | ---@field public add_flag fun(self: data.EntityPrototype, flag: string): data.EntityPrototype 7 | ---@field public remove_flag fun(self: data.EntityPrototype, flag: string): data.EntityPrototype 8 | ---@field public has_flag fun(self: data.EntityPrototype, flag: string): boolean 9 | ``` 10 | 11 | ## Example Usage 12 | `ENTITY` function can be used like this to create a new entity: 13 | ```lua 14 | ENTITY { 15 | type = "assembling-machine", 16 | name = "example-entity", 17 | icon = "__pyexample__/graphics/icons/example.png", 18 | icon_size = 64, 19 | ... 20 | } 21 | ``` 22 | It can also be used to get an existing entity: 23 | ```lua 24 | ENTITY("example-entity") 25 | ``` 26 | 27 | ## Entity Functions 28 | ### Add Flag 29 | `@field public add_flag fun(self: data.EntityPrototype, flag: string): data.EntityPrototype` 30 | ```lua 31 | local flag = "placeable-by-player" 32 | ENTITY("example-item"):add_flag(flag) 33 | ``` 34 | 35 | ### Remove Flag 36 | `@field public remove_flag fun(self: data.EntityPrototype, flag: string): data.EntityPrototype` 37 | ```lua 38 | local flag = "placeable-by-player" 39 | ENTITY("example-item"):remove_flag(flag) 40 | ``` 41 | 42 | ### Has Flag 43 | `@field public has_flag fun(self: data.EntityPrototype, flag: string): boolean` 44 | ```lua 45 | local flag = "placeable-by-player" 46 | local has_flag = ENTITY("example-item"):has_flag(flag) 47 | ``` 48 | -------------------------------------------------------------------------------- /src/internal_apis/pypostprocessing/fluid.md: -------------------------------------------------------------------------------- 1 | # Fluid 2 | *As of August 16th, 2025*
3 | `FLUID` can be used like so: 4 | ```lua 5 | FLUID{ 6 | type = "fluid", 7 | name = "example-fluid" 8 | } 9 | ``` 10 | You can also use it to get existing fluids 11 | ```lua 12 | FLUID("example-fluid") 13 | ``` 14 | -------------------------------------------------------------------------------- /src/internal_apis/pypostprocessing/item.md: -------------------------------------------------------------------------------- 1 | # Items 2 | *As of August 16th, 2025*
3 | ```lua 4 | ---@class data.ItemPrototype 5 | ---@field public add_flag fun(self: data.ItemPrototype, flag: string): data.ItemPrototype 6 | ---@field public remove_flag fun(self: data.ItemPrototype, flag: string): data.ItemPrototype 7 | ---@field public has_flag fun(self: data.ItemPrototype, flag: string): boolean 8 | ---@field public spoil fun(self: data.ItemPrototype, spoil_result: (string | table), spoil_ticks: number): data.ItemPrototype 9 | ``` 10 | 11 | ## Example Usage 12 | `ITEM` function can be used like this to create a new item: 13 | ```lua 14 | ITEM { 15 | type = "item", 16 | name = "example-item", 17 | icon = "__pyexample__/graphics/icons/example.png", 18 | icon_size = 64, 19 | stack_size = 50 20 | } 21 | ``` 22 | It can also be used like this to get an existing item: 23 | ```lua 24 | ITEM("example-item") 25 | ``` 26 | 27 | ## Item Functions 28 | ### Spoil 29 | `@field public spoil fun(self: data.ItemPrototype, spoil_result: (string | table), spoil_ticks: number): data.ItemPrototype` 30 | ```lua 31 | local spoil_result = "iron-plate" 32 | local spoil_ticks = 5 * 60 -- 5 Seconds 33 | ITEM("example-item"):spoil(spoil_result, spoil_ticks) 34 | ``` 35 | 36 | ### Add Flag 37 | `@field public add_flag fun(self: data.ItemPrototype, flag: string): data.ItemPrototype` 38 | ```lua 39 | local flag = "placeable-by-player" 40 | ITEM("example-item"):add_flag(flag) 41 | ``` 42 | 43 | ### Remove Flag 44 | `@field public remove_flag fun(self: data.ItemPrototype, flag: string): data.ItemPrototype` 45 | ```lua 46 | local flag = "placeable-by-player" 47 | ITEM("example-item"):remove_flag(flag) 48 | ``` 49 | 50 | ### Has Flag 51 | `@field public has_flag fun(self: data.ItemPrototype, flag: string): boolean` 52 | ```lua 53 | local flag = "placeable-by-player" 54 | local has_flag = ITEM("example-item"):has_flag(flag) 55 | ``` 56 | -------------------------------------------------------------------------------- /src/internal_apis/pypostprocessing/recipe.md: -------------------------------------------------------------------------------- 1 | # Recipe 2 | *As of August 16th, 2025*
3 | ```lua 4 | ---@class data.RecipePrototype 5 | ---@field public standardize fun(self: data.RecipePrototype): data.RecipePrototype 6 | ---@field public add_unlock fun(self: data.RecipePrototype, technology_name: string | string[]): data.RecipePrototype 7 | ---@field public remove_unlock fun(self: data.RecipePrototype, technology_name: string | string[]): data.RecipePrototype 8 | ---@field public replace_unlock fun(self: data.RecipePrototype, technology_old: string | string[], technology_new: string | string[]): data.RecipePrototype 9 | ---@field public replace_ingredient fun(self: data.RecipePrototype, old_ingredient: string, new_ingredient: string | data.IngredientPrototype, new_amount: integer?): data.RecipePrototype 10 | ---@field public add_ingredient fun(self: data.RecipePrototype, ingredient: data.IngredientPrototype): data.RecipePrototype 11 | ---@field public remove_ingredient fun(self: data.RecipePrototype, ingredient_name: string): data.RecipePrototype, integer 12 | ---@field public replace_result fun(self: data.RecipePrototype, old_result: string, new_result: string | data.ProductPrototype, new_amount: integer?): data.RecipePrototype 13 | ---@field public add_result fun(self: data.RecipePrototype, result: string | data.ProductPrototype): data.RecipePrototype 14 | ---@field public remove_result fun(self: data.RecipePrototype, result_name: string): data.RecipePrototype 15 | ---@field public clear_ingredients fun(self: data.RecipePrototype): data.RecipePrototype 16 | ---@field public multiply_result_amount fun(self: data.RecipePrototype, result_name: string, percent: number): data.RecipePrototype 17 | ---@field public multiply_ingredient_amount fun(self: data.RecipePrototype, ingredient_name: string, percent: number): data.RecipePrototype 18 | ---@field public add_result_amount fun(self: data.RecipePrototype, result_name: string, increase: number): data.RecipePrototype 19 | ---@field public add_ingredient_amount fun(self: data.RecipePrototype, ingredient_name: string, increase: number): data.RecipePrototype 20 | ---@field public set_result_amount fun(self: data.RecipePrototype, result_name: string, amount: number): data.RecipePrototype 21 | ---@field public set_ingredient_amount fun(self: data.RecipePrototype, ingredient_name: string, amount: number): data.RecipePrototype 22 | ---@field public get_main_product fun(self: data.RecipePrototype, allow_multi_product: bool?): LuaItemPrototype?|LuaFluidPrototype? 23 | ---@field public get_icons fun(self: data.RecipePrototype): data.IconData 24 | ``` 25 | 26 | ## Example Usage 27 | You can create a new recipe like so: 28 | ```lua 29 | RECIPE { 30 | type = "recipe", 31 | name = "example-recipe", 32 | ingredients = {}, 33 | results = {{type = "item", name = "example-item", amount = 1}} 34 | } 35 | ``` 36 | You can also get existing recipes 37 | ```lua 38 | RECIPE("example-recipe") 39 | ``` 40 | 41 | ## Recipe Functions 42 | ### Add Unlock 43 | `@field public add_unlock fun(self: data.RecipePrototype, technology_name: string | string[]): data.RecipePrototype` 44 | ```lua 45 | local technology_name = "example-tech" 46 | RECIPE("example-recipe"):add_unlock(technology_name) 47 | ``` 48 | ```lua 49 | local technologies = {"example-tech", "example-tech-2"} 50 | RECIPE("example-recipe"):add_unlock{technologies} 51 | ``` 52 | 53 | ### Remove Unlock 54 | `@field public remove_unlock fun(self: data.RecipePrototype, technology_name: string | string[]): data.RecipePrototype` 55 | ```lua 56 | local technology_name = "example-tech" 57 | RECIPE("example-recipe"):remove_unlock(technology_name) 58 | ``` 59 | ```lua 60 | local technologies = {"example-tech", "example-tech-2"} 61 | RECIPE("example-recipe"):remove_unlock{technologies} 62 | ``` 63 | 64 | ### Replace Unlock 65 | `@field public replace_unlock fun(self: data.RecipePrototype, technology_old: string | string[], technology_new: string | string[]): data.RecipePrototype` 66 | 67 | Equivalent to `:remove_unlock(from):add_unlock(to)` 68 | ```lua 69 | local from = "example-tech" 70 | local to = "ultra-tech" 71 | RECIPE("example-recipe"):replace_unlock(from, to) 72 | ``` 73 | ```lua 74 | local from = {"example-tech", "example-tech-2"} 75 | local to = {"ultra-tech", "ultra-tech-2"} 76 | RECIPE("example-recipe"):replace_unlock(from, to) 77 | ``` 78 | 79 | ### Add Ingredient 80 | `@field public add_ingredient fun(self: data.RecipePrototype, ingredient: data.IngredientPrototype): data.RecipePrototype` 81 | ```lua 82 | RECIPE("example-recipe"):add_ingredient{ 83 | type = "item", 84 | name = "iron-plate", 85 | amount = 5 86 | } 87 | ``` 88 | 89 | ### Remove Ingredient 90 | `@field public remove_ingredient fun(self: data.RecipePrototype, ingredient_name: string): data.RecipePrototype, integer` 91 | ```lua 92 | RECIPE("example-recipe"):remove_ingredient("iron-plate") 93 | ``` 94 | 95 | ### Replace Ingredient 96 | `@field public replace_ingredient fun(self: data.RecipePrototype, old_ingredient: string, new_ingredient: string | data.IngredientPrototype, new_amount: integer?): data.RecipePrototype` 97 | ```lua 98 | local from = "iron-plate" 99 | local to = "copper-plate" 100 | RECIPE("example-recipe"):replace_ingredient(from, to) 101 | ``` 102 | ```lua 103 | local from = "iron-plate" 104 | local to = { 105 | type = "item", 106 | name = "copper-plate", 107 | amount = 5 108 | } 109 | RECIPE("example-recipe"):replace_ingredient(from, to) 110 | ``` 111 | ```lua 112 | local from = "iron-plate" 113 | local to = "copper-plate" 114 | local amount = 10 115 | RECIPE("example-recipe"):replace_ingredient(from, to, amount) 116 | ``` 117 | 118 | ### Clear Ingredients 119 | `@field public clear_ingredients fun(self: data.RecipePrototype): data.RecipePrototype` 120 | ```lua 121 | RECIPE("example-recipe"):clear_ingredients() 122 | ``` 123 | 124 | ### Add Result 125 | `@field public add_result fun(self: data.RecipePrototype, result: string | data.ProductPrototype): data.RecipePrototype` 126 | ```lua 127 | RECIPE("example-recipe"):add_result{ 128 | type = "item", 129 | name = "iron-plate", 130 | amount = 5 131 | } 132 | ``` 133 | 134 | ### Remove Result 135 | `@field public remove_result fun(self: data.RecipePrototype, result_name: string): data.RecipePrototype` 136 | ```lua 137 | RECIPE("example-recipe"):remove_result("iron-plate") 138 | ``` 139 | 140 | ### Replace Result 141 | `@field public replace_result fun(self: data.RecipePrototype, old_result: string, new_result: string | data.ProductPrototype, new_amount: integer?): data.RecipePrototype` 142 | ```lua 143 | local from = "iron-plate" 144 | local to = "copper-plate" 145 | RECIPE("example-recipe"):replace_result(from, to) 146 | ``` 147 | ```lua 148 | local from = "iron-plate" 149 | local to = { 150 | type = "item", 151 | name = "copper-plate", 152 | amount = 5 153 | } 154 | RECIPE("example-recipe"):replace_result(from, to) 155 | ``` 156 | ```lua 157 | local from = "iron-plate" 158 | local to = "copper-plate" 159 | local amount = 10 160 | RECIPE("example-recipe"):replace_result(from, to, amount) 161 | ``` 162 | 163 | ### Multiply Result Amount 164 | `@field public multiply_result_amount fun(self: data.RecipePrototype, result_name: string, percent: number): data.RecipePrototype` 165 | ```lua 166 | local name = "iron-plate" 167 | local percent = 1.5 168 | RECIPE("example-recipe"):multiply_result_amount(name, percent) 169 | ``` 170 | 171 | ### Multiply Ingredient Amount 172 | `@field public multiply_ingredient_amount fun(self: data.RecipePrototype, ingredient_name: string, percent: number): data.RecipePrototype` 173 | ```lua 174 | local name = "iron-plate" 175 | local percent = 1.5 176 | RECIPE("example-recipe"):multiply_ingredient_amount(name, percent) 177 | ``` 178 | 179 | ### Add Result Amount 180 | `@field public add_result_amount fun(self: data.RecipePrototype, result_name: string, increase: number): data.RecipePrototype` 181 | ```lua 182 | local name = "iron-plate" 183 | local increase = 10 184 | RECIPE("example-recipe"):add_result_amount(name, increase) 185 | ``` 186 | 187 | ### Add Ingredient Amount 188 | `@field public add_ingredient_amount fun(self: data.RecipePrototype, ingredient_name: string, increase: number): data.RecipePrototype` 189 | ```lua 190 | local name = "iron-plate" 191 | local increase = 10 192 | RECIPE("example-recipe"):add_ingredient_amount(name, increase) 193 | ``` 194 | 195 | ### Set Result Amount 196 | `@field public set_result_amount fun(self: data.RecipePrototype, result_name: string, amount: number): data.RecipePrototype` 197 | ```lua 198 | local name = "iron-plate" 199 | local amount = 10 200 | RECIPE("example-recipe"):set_result_amount(name, amount) 201 | ``` 202 | 203 | ### Set Ingredient Amount 204 | `@field public set_ingredient_amount fun(self: data.RecipePrototype, ingredient_name: string, amount: number): data.RecipePrototype` 205 | ```lua 206 | local name = "iron-plate" 207 | local amount = 10 208 | RECIPE("example-recipe"):set_ingredient_amount(name, amount) 209 | ``` 210 | 211 | ### Get Main Product 212 | `@field public get_main_product fun(self: data.RecipePrototype, allow_multi_product: bool?): LuaItemPrototype?|LuaFluidPrototype?` 213 | ```lua 214 | local main_product = RECIPE("example-recipe"):get_main_product() 215 | ``` 216 | ```lua 217 | local main_products = RECIPE("example-recipe"):get_main_product(true) 218 | ``` 219 | 220 | ### Get Icons 221 | `@field public get_icons fun(self: data.RecipePrototype): data.IconData` 222 | ```lua 223 | local icons = RECIPE("example-recipe"):get_icons() 224 | ``` 225 | -------------------------------------------------------------------------------- /src/internal_apis/pypostprocessing/technology.md: -------------------------------------------------------------------------------- 1 | # Technology 2 | # WIP 3 | 4 | ```lua 5 | ``` 6 | 7 | ## Example Usage 8 | 9 | ## Technology Functions 10 | -------------------------------------------------------------------------------- /src/internal_apis/pypostprocessing/template.md: -------------------------------------------------------------------------------- 1 | # PLACEHOLDER 2 | ```lua 3 | ``` 4 | 5 | ## Example Usage 6 | 7 | ## PLACEHOLDER Functions 8 | -------------------------------------------------------------------------------- /src/internal_apis/pypostprocessing/tile.md: -------------------------------------------------------------------------------- 1 | # Tile 2 | # WIP 3 | ```lua 4 | ``` 5 | 6 | ## Example Usage 7 | 8 | ## Tile Functions 9 | -------------------------------------------------------------------------------- /src/internal_apis/pystellarexpedition/README.md: -------------------------------------------------------------------------------- 1 | # PyStellarExpedition 2 | 3 | Some of our internal APIs written done for developers, maybe you can gleam some useful information. 4 | -------------------------------------------------------------------------------- /src/internal_apis/pystellarexpedition/rocket-modules.md: -------------------------------------------------------------------------------- 1 | # Rocket Modules 2 | *As of August 16th, 2025* 3 | 4 | ## Data 5 | 6 | We define the equipment grid and the equipment category and the equipment grid proxy which is just an invisible car. 7 | 8 | Then we can create our equipments with custom definition and with some prefilled in stuff to help. We store the equipment size and equipment name in a global variable for use later. Then we can define the equipment, each equipment is essentially a dummy. We define an item, a `battery-equipment`, and a recipe for each using the `create_equipment` function. 9 | 10 | `create_equipment` looks like this: 11 | ```lua 12 | --- @param definition ItemEquipmentRecipeHybrid 13 | --- 14 | --- @class ItemEquipmentRecipeHybrid 15 | --- @field name string 16 | --- @field icon string|nil 17 | --- @field icon_size number|nil Default size is 64 18 | --- @field stack_size number|nil Default size is 10 19 | --- @field shape table 20 | --- @field hidden bool|nil 21 | --- @field categories string[] The categories of the equipment 22 | --- @field category string The recipe category 23 | --- @field ingredients table|nil 24 | --- @field results table|nil 25 | local function create_equipment(definition) 26 | -- ... 27 | end 28 | ``` 29 | `definition` is a mixture of item, equipment and recipe prototype. Example: 30 | ```lua 31 | create_equipment{ 32 | name = "lander", 33 | icon = "__pystellarexpeditiongraphics__/graphics/icons/lander-module.png", 34 | icon_size = 64, 35 | categories = {"rocket-silo-equipment"}, 36 | stack_size = 1, 37 | shape = { 38 | width = 2, 39 | height = 2, 40 | type = "full" 41 | }, 42 | } 43 | ``` 44 | 45 | After we defined equipment we can now define the rocket part recipes for them using the local `iterate_counts` function. Which has this definition: 46 | ```lua 47 | --- Creates a loop like this: 48 | --- a: 1, b: 0, c: 0 49 | --- a: 2, b: 0, c: 0 50 | --- a: 3, b: 0, c: 0 51 | --- a: 0, b: 1, c: 0 52 | --- a: 1, b: 1, c: 0 53 | --- Essentially binary counting 54 | --- @param labels string[] 55 | --- @param max number 56 | --- @param print_func fun(row: table) A table of labels and numbers 57 | --- @param include_zero bool Whether to include zeros in the print_func row param 58 | local function iterate_counts(labels, max, print_func, include_zero) 59 | -- ... 60 | end 61 | ``` 62 | We use this function to create every possible `rocket-part` recipe and trim out some that can't exist using `get_size` 63 | ```lua 64 | --- Gets the size of an equipment 65 | --- @param name string 66 | local function get_size(name) 67 | -- ... 68 | end 69 | ``` 70 | This uses one of the globals we talked about earlier. 71 | 72 | ### Problems with get_size and trimming 73 | Currently we're doing it stupidly which means just not really trimming anything which leads with lots of impossible recipes. This isn't necessarily bad but still doesn't feel good. 74 | 75 | I think implementing: [https://en.wikipedia.org/wiki/Knuth's_Algorithm_X](https://en.wikipedia.org/wiki/Knuth%27s_Algorithm_X), Would be a great step in the right direction. 76 | 77 | `get_size` also doesn't account for custom shapes which we will have in the future. 78 | 79 | ## Control 80 | 81 | ### WIP 82 | --------------------------------------------------------------------------------