diff --git a/README.md b/README.md
index 772daf8..be4594a 100644
--- a/README.md
+++ b/README.md
@@ -45,13 +45,13 @@ get extra copies when clicking "restart," so here's a canonical example. It's
straightforward to edit this to change or add more abilities if you need to.
## Factory automation
-**v3.2.2**
+**v3.2.3**
This set of scripts manages auto-crafting of everything in the factory. It is an (extremely rewritten) fork of Xenos' factory package, with 1/5 the size, 1/3 the scripts, only **16** maximum actions and scannable item-groups built-in to the base package.
`Import code:`
```
-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
+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
```
**This requires `turbo exec v2.2` to function, found here: https://github.com/d0sboots/TPT2_scripts/blob/main/common/turbo_exec/README.md**
diff --git a/factory/README.md b/factory/README.md
index 4219cea..6c8fa4a 100644
--- a/factory/README.md
+++ b/factory/README.md
@@ -12,7 +12,7 @@ to do data and code generation in Lua. It also `:import`s four files that are ne
The editor has a source-import feature, used the same way you import scripts.
The following will set up a new workspace for the factory:
```
-{"workspaces":{"D0S.Factory":[["factory constants",":import factory macros\n\n#version v3.2.2\n\n; Everything in the list below is a valid target for automation, and can be\n; assigned to the \"target_type\" variable in \"lanuch factory craft\".\n; For example, \"make(oven, 10, \"machine.oven\")\" indicates that \"oven\" is a valid\n; item, usable from tiers 1-10, with the internal game name of \"machine.oven\"\n; (which you don't need to worry about.)\n; Thus you can enter \"oven\" in the box in the in-game editor (without the quotes).\n\n; If you add or change this list, mind this constraint:\n; * All the crafted items have to come before the rest of the items.\n; * All the item-groups must be in a single block.\n; * Prefer keeping things in alphabetical order within categories, because\n; they are ordered the same way when presented in-game.\n\n; ===== VALID IDS =====\n\n; *** To reiterate, just use the name part (i.e. \"white_producer\"), not the whole thing! ***\n\n; ----- CRAFTED ITEMS -----\n\n; Producers, named by building, tiers 1-5\n{make(arcade_producer, 5, \"producer.arcade\")}\n{make(construction_firm_producer, 5, \"producer.constructionFirm\")}\n{make(exotic_producer, 1, \"producer.exoticgems\")}\n{make(factory_producer, 5, \"producer.factory\")}\n{make(gem_producer, 1, \"producer.gems\")}\n{make(headquarters_producer, 5, \"producer.headquarters\")}\n{make(laboratory_producer, 5, \"producer.laboratory\")}\n{make(mine_producer, 5, \"producer.mine\")}\n{make(museum_producer, 5, \"producer.museum\")}\n{make(powerplant_producer, 5, \"producer.powerplant\")}\n{make(pumpkin_producer, 1, \"pumpkin.producer\")}\n{make(shipyard_producer, 5, \"producer.shipyard\")}\n{make(statue_of_cubos_producer, 5, \"producer.statueofcubos\")}\n{make(town_producer, 5, \"producer.town\")}\n{make(tradingpost_producer, 5, \"producer.tradingpost\")}\n{make(workshop_producer, 5, \"producer.workshop\")}\n\n; Boosters, tiers 1-3\n{make(acceleration_booster, 3, \"booster.acceleration\")}\n{make(machine_booster, 3, \"booster.machines\")}\n{make(production_booster, 3, \"booster.production.regular\")}\n{make(resource_booster, 3, \"booster.resource.drops\")}\n\n{category(prod, town_producer, arcade_producer, resource_booster)}\n\n; Machines, tiers 1-10\n{make(assembly, 10, \"machine.assembler\")}\n{make(belt, 10, \"machine.transportbelt\")}\n{make(boiler, 10, \"machine.boiler\")}\n{make(crusher, 10, \"machine.crusher\")}\n{make(cutter, 10, \"machine.cutter\")}\n{make(mixer, 10, \"machine.mixer\")}\n{make(oven, 10, \"machine.oven\")}\n{make(presser, 10, \"machine.presser\")}\n{make(refiner, 10, \"machine.refinery\")}\n{make(shaper, 10, \"machine.shaper\")}\n\n{category(mach, belt, assembly, shaper)}\n\n; Various crafted parts\n{make(block, 10, \"block\")}\n{make(chip, 5, \"chip\")}\n{make(hammer, 1, \"hammer\")}\n{make(insul_cable, 10, \"cable.insulated\")}\n{make(motor, 10, \"motor\")}\n{make(pump, 10, \"pump\")}\n{make(rainbow_dust, 1, \"dust.rainbow\")}\n{make(rubber_sapling, 1, \"sapling.rubber\")}\n{make(stacked_plate, 10, \"plate.stack\")}\n{make(stacked_pumpkin, 1, \"pumpkin.stack\")}\n{make(void_sapling, 1, \"sapling.void\")}\n\n{category(crft, chip, block, void_sapling)}\n\n; Transforms ore into dust, tiers 1-10\n; Because of an implementation detail, this must come before any\n; scannable items.\n; This doesn't show up in any category in the UI.\n{special(ore, 10, \"ore\")}\n\n; ----- SCANNABLE ITEM GROUPS -----\n; These are not real items, but rather groups of items that will be made together\n; if you select one of these names. They are meant for use with the Crafter, to\n; crank out sets of items to scan quickly. You will want to set the quantity to 1000.\n\n; Notes on using item groups:\n; * SCAN YOUR ORES FIRST! The factory can and will consume your ore to make stuff,\n; and it takes a long time to get 1000 T10 ore.\n; * The quantity works a little differently than normal items. Instead of making\n; 1000 items each time, running it again will top off everything to be\n; *at* the quantity of 1000 items.\n; * If you craft a higher tier after a lower tier, it will consume the results\n; of the first craft to make the higher tier. Scan all the items first, before\n; moving on to the next tier!\n; * None of these groups include Gem Producers or Exotic Producers. They are too\n; expensive in comparison to other things, craft them on your own when you\n; judge the time is right.\n\n; Makes *everything* (expect special producers and lumps) of the given tier.\n; This is the combination of \"chips_and_prods\", \"machines\", and \"parts\".\n; Requires x10 and high processing speed to have a hope of completing in a\n; reasonable amount of time for T5 and T10.\n; You need Quantum Warehouse to have enough space for this at higher tiers!\n{group(all, 10)}\n\n; All the tiers of chips. Warning: Expensive. Input this as tier 1.\n{group(chips, 1)}\n\n; Producers + the chip of the corresponding tier. This is everything that\n; exists in tier range 1-5, for convenience.\n{group(chip_and_prods, 5)}\n\n; All machines. Tiers 1-10\n{group(machines, 10)}\n\n; All ingredients and parts. Tiers 1-10. Doesn't include ore (scan that before\n; starting) or lumps (due to technical limitations). Lumps aren't required\n; for anything currently though.\n; Includes rubber plates at tier 1 and hammers at tier *2*.\n; Rubber trees are at tier 9 because of their ore.\n{group(parts, 10)}\n\n; All producers, except for special producers. Tiers 1-5\n{group(producers, 5)}\n\n{category(grup, all, all, producers)}\n\n; ----- INGREDIENTS AND PRODUCED PARTS -----\n\n; Parts, tiers 1-10\n{item(anti_pumpkin, 1, \"pumpkin.anti\")}\n{item(board, 10, \"plate.circuit\")}\n{item(cable, 10, \"cable\")}\n{item(carved_pumpkin, 1, \"pumpkin.carved\")}\n{item(circuit, 10, \"circuit\")}\n{item(dense_block, 10, \"block.dense\")}\n{item(dense_plate, 10, \"plate.dense\")}\n{item(ingot, 10, \"ingot\")}\n{item(pipe, 10, \"pipe\")}\n{item(plate, 10, \"plate\")}\n{item(pumpkin_plate, 1, \"pumpkin.plate\")}\n{item(rainbow_ingot, 1, \"ingot.rainbow\")}\n{item(rainbow_plate, 1, \"plate.rainbow\")}\n{item(ring, 10, \"ring\")}\n{item(rod, 10, \"rod\")}\n{item(rubber_plate, 1, \"plate.rubber\")}\n{item(screw, 10, \"screw\")}\n{item(wire, 10, \"wire\")}\n\n{category(part, circuit, anti_pumpkin, wire)}\n\n;Tries to make dust from ores and lower-tier dusts, tiers 1-10\n; Doesn't appear in the UI\n{item(dust, 10, \"dust\")}\n\n; Tiers up dust, tiers 1-9\n; These are ore lumps, plus putting them into the mixer.\n; There is no \"only make ore lumps, without mixing them\" option right now.\n; Doesn't appear in the UI\n{special(lump, 9, \"lump\")}\n\n; Can only be made from saplings\n; Doesn't appear in the UI\n{item(void_essence, 1, \"essence.void\")}\n\n; Can only be gotten through the Halloween event\n; Doesn't appear in the UI\n{item(pumpkin, 1, \"pumpkin\")}\n\n; Rubber, tier 1 only\n; Doesn't appear in the UI\n{item(rubber, 1, \"rubber\")}\n\n; ===== END OF VALID IDS =====\n"],["factory macros","; Infrastructure for producing all the constants in the factory package.\n; Since this is :imported, it can't produce any instructions.\n\n#script_name(name) D0S.factory {version}:{name}\n\n; Key assignments. You can edit these here, or you can edit them later in the\n; generated script (but you will have more places to modify).\n#up w\n#down s\n#left a\n#right d\n#start f\n\n; Shared hidden variables for the UI\n#action \"fa#\"\n#status \"fs#\"\n\n; Produces the name of the queue variable for the given tier and item.\n#queue_str \"cq\"\n#queue(tier, item) -1 + 10 * ({item}) + {tier}\n\n; Raw access to the queue. Not preferred, but better than using \"queue\"\n; directly. Use for low-level optimization.\n#get_raw(raw_item) global.double.get({queue_str} . (100 + {raw_item}))\n#set_raw(raw_item, value) global.double.set({queue_str} . (100 + {raw_item}), {value})\n\n; Helper macros for operating on the craft_queue. These should be\n; used instead of direct access whenever possible.\n#get_queue(tier, item) {get_raw({queue({tier},{item})})}\n#set_queue(tier, item, value) {set_raw({queue({tier},{item})},{value})}\n\n; Add/subtract a value from the queue. Although it expands to the same thing,\n; it saves a lot of boilerplate in the source and makes it easier to understand\n; what's going on.\n#add_queue(tier, item, value) {set_queue({tier},{item},{get_queue({tier},{item})} + {value})}\n#subtract_queue(tier, item, value) {set_queue({tier},{item},{get_queue({tier},{item})} - ({value}))}\n\n; Used with factory.composite_string to extract the name from the\n; returned data.\n#get_name sub(itemdata, 0, index(itemdata, \" \", 0))\n\n; These macros can only be called *after* factory.composite_string,\n; i.e. later on in the script.\n#items_count {lua(return factory.items_count)}\n#tiers_char sub(itemdata, {lua(return factory.entry_size - 1)}, 1)\n\n{lua(\\\n factory = {};\\\n factory.items = {};\\\n factory.item_names = {};\\\n factory.categories = {};\\\n factory.prods = {};\\\n factory.prod_machines = {};\\\n factory.name_max_size = 0;\\\n factory.game_name_sizes = {};\\\n factory.game_name_sizes.item = 0;\\\n factory.game_name_sizes.craft = 0;\\\n factory.game_name_sizes.group = 0;\\\n factory.game_name_sizes.special = 0;\\\n factory.group_map = {};\\\n factory.group_map.item = 0;\\\n factory.group_map.craft = 1;\\\n factory.group_map.group = 2;\\\n factory.group_map.special = 2; --[[Intentionally the same]]\\\n\\\n function factory.add_item(name, tiers, game_name, craft_type)\\\n local item = {};\\\n item.id = #factory.items + 1;\\\n if item.id > 89 then\\\n return \"Too many items: Over the limit while adding \" .. name;\\\n end\\\n item.name = name;\\\n item.game_name = game_name;\\\n item.tier = tiers;\\\n item.craft_type = craft_type;\\\n factory.items[item.id] = item;\\\n factory.item_names[name] = item;\\\n if #name > factory.name_max_size then\\\n factory.name_max_size = #name;\\\n end\\\n if #game_name > factory.game_name_sizes[craft_type] then\\\n factory.game_name_sizes[craft_type] = #game_name;\\\n end\\\n return \":const int \" .. name .. \" \" .. #factory.items;\\\n end\\\n\\\n function factory.composite_string(var, filter, use_group_info)\\\n local acc = {};\\\n local size = 0;\\\n local first = #factory.items;\\\n local last = 0;\\\n local group_map = factory.group_map;\\\n \\\n if filter == nil then\\\n size = math.max(factory.game_name_sizes[\"item\"], factory.game_name_sizes[\"craft\"]);\\\n else\\\n size = factory.game_name_sizes[filter];\\\n end\\\n size = size + 1;\\\n local fmt = \"%-\" .. size .. \"s%d\";\\\n size = size + 1;\\\n acc[1] = 'sub(\"';\\\n for i = 1, #factory.items do\\\n local item = factory.items[i];\\\n if filter == nil or item.craft_type == filter then\\\n if use_group_info then\\\n acc[#acc + 1] = string.format(fmt, item.game_name, group_map[item.craft_type]);\\\n else\\\n acc[#acc + 1] = string.format(fmt, item.game_name, item.tier - 1);\\\n end\\\n if i < first then first = i end\\\n if i > last then last = i end\\\n end\\\n end\\\n acc[#acc + 1] = '\", ' .. size .. \" * (\" .. var .. \"), \" .. size .. ')';\\\n factory.items_count = last - first + 1;\\\n factory.entry_size = size;\\\n return table.concat(acc);\\\n end\\\n\\\n function factory.add_category(name, ...)\\\n local category = {}\\\n factory.categories[#factory.categories + 1] = category\\\n category.name = name\\\n local name1, name2, name3 = \"default\", \"first\", \"last\"\\\n local arg1, arg2, arg3 = ...\\\n for i = 1, 3 do\\\n local trimmed = arg1:gsub(\"^ +\", \"\")\\\n local item = factory.item_names[trimmed]\\\n if not item then\\\n return \"Can't find \" .. trimmed\\\n end\\\n category[name1] = item.id\\\n name1, name2, name3 = name2, name3\\\n arg1, arg2, arg3 = arg2, arg3\\\n end\\\n end\\\n)}\n\n; Define a new production item with name \"name\", craftable from tiers 1-\"tiers\".\n; The string passed to produce() is \"game_name\".\n; It will take the next available id, starting from 1.\n#item(name, tiers, game_name) {lua(\\\n return factory.add_item(\"{name}\", {tiers}, {game_name}, \"item\")\\\n)}\n\n; Define a new production item with name \"name\", craftable from tiers 1-\"tiers\".\n; The string passed to craft() is \"game_name\".\n; It will take the next available id, starting from 1.\n#make(name, tiers, game_name) {lua(\\\n return factory.add_item(\"{name}\", {tiers}, {game_name}, \"craft\")\\\n)}\n\n; Define a new item group, i.e. a set of items designed to be scanned in the\n; crafter. Because items need a valid game_name to be passed to count() in\n; various places (or else there will be spurious logging), we use \"ore\".\n; (The value returned from count() will always be ignored.)\n#group(name, tiers) {lua(\\\n return factory.add_item(\"{name}\", {tiers}, \"ore\", \"group\")\\\n)}\n\n; Defines a \"special\" item. This has a real name, like an item, but acts\n; like an item-group in other ways. This is used for lumps and ore, which\n; require special-case handling.\n#special(name, tiers, game_name) {lua(\\\n return factory.add_item(\"{name}\", {tiers}, {game_name}, \"special\")\\\n)}\n\n; Defines a category grouping for the UI. \"First\" and \"last\" are the span of\n; the group (inclusive), in item names. Must be called *after* the relevant items\n; are defined.\n#category(name, default, first, last) {lua(\\\n return factory.add_category(\"{name}\", \"{default}\", \"{first}\", \"{last}\")\\\n)}\n"],["worker_storage_lib","; This library will find a slot in the pool of workers to use as \"permanent storage\".\n; If one doesn't already exist with the given tag, a new one will be allocated,\n; *but* it will avoid overwriting other worker names using the same sytem of tags\n; for storage. Thus, scripts can use this to cooperate without having to\n; explicitly assign worker slot numbers.\n;\n; The scheme is that a name will be prefixed with \"[TOKEN]\", where \"TOKEN\" is\n; some short, unique word that will identify the script in question. For instance,\n; the factory might use the word \"factory\".\n;\n; This library does not actually store anything in the worker name. Instead, it\n; sets the variable \"worker_slot\", which you must declare in your code.\n; If worker_slot < 100, then it found a valid slot with the worker_prefix.\n; If 100 <= worker_slot < 200, then there was no existing slot, but new data\n; can be initialized at (worker_slot - 100). If worker_slot == 200, then there\n; are no available slots at all, and you should show an error message or take\n; approriate action.\n;\n; A complete example looks like this (replace angle brackets with curlies):\n;\n; :local int worker_slot\n; \n; \n\n; =================================================================================\n\n#worker_prefix {lua(return worker_lib_prefix)}\n#worker_lacks_prefix sub(worker.name(worker_slot), 0, {len({worker_prefix})}) != \"{worker_prefix}\"\n\n; The first macro, which defines the first line and the loop variable that we\n; jump to. We also store the worker_prefix with Lua, so that we don't need to\n; repeat it on the 2nd macro.\n; We don't want to skip slot 0. Doing it like this saves a line.\n#worker_lib_line_1(worker_prefix_arg) {lua(\\\n worker_lib_prefix = \"{worker_prefix_arg}\"\\\n)}\\\nfind_worker_slot_loop: worker_slot = worker_slot + if({worker_lacks_prefix}, 1, 0)\n\n; The second macro, which iterates until we're done.\n; The test against worker.group() checks for non-existant workers. They would\n; otherwise show as acceptable candidates, and we need to keep iterating to 200\n; in that case.\n#worker_lib_line_2 gotoif(find_worker_slot_loop,\\\n worker_slot < 100 && {worker_lacks_prefix} ||\\\n worker_slot >= 100 && worker_slot < 200 && (worker.group(worker_slot - 100) == -1 || contains(sub(worker.name(worker_slot - 100), 0, 1), \"[\")))\n"],["recipes",":import factory constants\n\n{lua(\\\n factory.recipes = {};\\\n)}\n\n; Infrastructure for defining the recipe list.\n{lua(\\\n function factory.recipe(item)\\\n local recipe_item = factory.item_names[item];\\\n if not recipe_item then\\\n return \"Unknown item '\" .. item .. \"'\";\\\n end\\\n if recipe_item.recipes then\\\n return \"Duplicate recipe for '\" .. item .. \"'\";\\\n end\\\n factory.recipe_item = recipe_item;\\\n local offset = 10 * recipe_item.id;\\\n --[[There are two types of recipe list that we store: A \"forward\" list stored\\\n on the item itself, which records what items are needed to *make* that\\\n item, and a \"reverse\" list stored on the main factory object and indexed\\\n by queue id, which records what items can be *made from* the item.\\\n The forward list is used by the item group system, while the reverse list\\\n is used by the recipe system proper.]]\\\n for i = offset, offset + recipe_item.tier - 1 do\\\n if not factory.recipes[i] then\\\n factory.recipes[i] = {};\\\n end\\\n end\\\n recipe_item.recipes = {};\\\n for i = 1, recipe_item.tier do\\\n recipe_item.recipes[i] = {};\\\n end\\\n end\\\n\\\n function factory.component(tiers, item_name, counts)\\\n local recipe_item = factory.recipe_item;\\\n if not recipe_item then\\\n return \"Tried to define a component before calling recipe!\"\\\n end\\\n local item_name = item_name:gsub(\" \", \"\");\\\n local item = factory.item_names[item_name];\\\n if not item then\\\n return \"Unknown item '\" .. item_name .. \"'\";\\\n end\\\n if item.craft_type == \"group\" then\\\n return string.format(\\\n [[Cannot add item \"%s\" because it is a group]],\\\n item.name,\\\n recipe_item.name,\\\n recipe_item.craft_type\\\n );\\\n end\\\n local tier_tmp = {};\\\n for word in tiers:gmatch(\"%S+\") do\\\n local tier = tonumber(word);\\\n if not tier then\\\n return \"Bad tier \" .. word\\\n end\\\n tier_tmp[#tier_tmp + 1] = tier;\\\n end\\\n if #tier_tmp ~= 1 and #tier_tmp ~= recipe_item.tier then\\\n return \"Tiers list has wrong size, item \" ..\\\n recipe_item.name .. \" has \" .. recipe_item.tier .. \" tiers\";\\\n end\\\n \\\n local base_tier;\\\n for i = 1, recipe_item.tier do\\\n base_tier = tier_tmp[i] or base_tier;\\\n local tier = base_tier;\\\n if tier < 1 then\\\n tier = tier + i;\\\n end\\\n tier_tmp[i] = tier;\\\n end\\\n \\\n local counts_tmp = {};\\\n for word in counts:gmatch(\"%S+\") do\\\n local count = tonumber(word);\\\n if not count then\\\n return \"Bad count \" .. word\\\n end\\\n counts_tmp[#counts_tmp + 1] = count;\\\n end\\\n if #counts_tmp ~= 1 and #counts_tmp ~= recipe_item.tier then\\\n return \"Counts list has wrong size, item \" ..\\\n recipe_item.name .. \" has \" .. recipe_item.tier .. \" tiers\";\\\n end\\\n for i = #counts_tmp + 1, recipe_item.tier do\\\n counts_tmp[i] = counts_tmp[i-1];\\\n end\\\n --[[Finally, merge the two expanded lists and distribute the results\\\n across the recipes for each tier. These recipes are \"reversed,\"\\\n in the sense that they don't describe how to make each item, but\\\n rather all the items that this one can be used to help make.\\\n It's this reversed list that is needed for computing the material\\\n needs when crafting.]]\\\n local recipes = factory.recipes;\\\n local offset = recipe_item.id * 10 - 1;\\\n for i = 1, recipe_item.tier do\\\n local tier = tier_tmp[i];\\\n local count = counts_tmp[i];\\\n if count > 0 and tier > 0 then\\\n --[[Tiers less than 1 can happen naturally, due to the negative tier\\\n convention, so they are silently dropped. Tiers greater than the\\\n the maximum number of tiers are an error.]]\\\n if tier > item.tier then\\\n return \"Tier \" .. tier .. \" > item max tier \" .. item.tier;\\\n end\\\n local idx = item.id * 10 + tier - 1;\\\n local comp = recipes[idx];\\\n if comp == nil then\\\n comp = {};\\\n recipes[idx] = comp;\\\n end\\\n comp[#comp + 1] = offset + i;\\\n comp[#comp + 1] = count;\\\n local item_recipe = recipe_item.recipes[i];\\\n item_recipe[#item_recipe + 1] = idx;\\\n item_recipe[#item_recipe + 1] = count;\\\n end\\\n end\\\n end\\\n\\\n function factory.produce(item, source, mult, machine)\\\n local recipe_item = factory.item_names[item];\\\n if not recipe_item then\\\n return \"Unknown item '\" .. item .. \"'\";\\\n end\\\n if recipe_item.recipes then\\\n return \"Duplicate recipe for '\" .. item .. \"'\";\\\n end\\\n local source_item = factory.item_names[source];\\\n if not source_item then\\\n return \"Unknown item '\" .. source .. \"'\";\\\n end\\\n if source_item.tier ~= recipe_item.tier then\\\n return \"Tier mismatch: \" .. source .. \"(\" .. source_item.tier ..\\\n \") vs \" .. item .. \"(\" .. recipe_item.tier .. \")\"\\\n end\\\n if #factory.prods == 0 or factory.prods[#factory.prods].machine ~= machine then\\\n if factory.prod_machines[machine] then\\\n return \"Found 2nd group for machine '\" .. machine .. \"'\";\\\n end\\\n factory.prod_machines[machine] = #factory.prods + 1;\\\n factory.prod_machines[#factory.prod_machines + 1] = machine;\\\n end\\\n local prod = {};\\\n prod.item = recipe_item;\\\n prod.source = source_item;\\\n prod.mult = mult;\\\n prod.machine = machine;\\\n factory.prods[#factory.prods + 1] = prod;\\\n\\\n local offset = 10 * source_item.id;\\\n local count = 1 / mult;\\\n for i = offset, offset + recipe_item.tier - 1 do\\\n if not factory.recipes[i] then\\\n factory.recipes[i] = {};\\\n end\\\n local recipe = factory.recipes[i];\\\n recipe[#recipe + 1] = 10 * recipe_item.id - offset + i;\\\n recipe[#recipe + 1] = count;\\\n end\\\n recipe_item.recipes = {};\\\n for i = 1, recipe_item.tier do\\\n local recipe = {};\\\n recipe[1] = offset + i - 1;\\\n recipe[2] = count;\\\n recipe_item.recipes[i] = recipe;\\\n end\\\n end\\\n\\\n function factory.check_recipes()\\\n for i = 1, #factory.items do\\\n local item = factory.items[i];\\\n if not item.recipes then\\\n return item.name .. \"(\" .. item.id .. \") has no assigned recipe!\";\\\n end\\\n end\\\n end\\\n)}\n\n; Every recipe begins with this line. There must be one (and only one)\n; recipe declaration for each item, even if there is no way to make it\n; (like for rubber).\n#recipe(item) {lua(return factory.recipe([[{item}]]))}\n\n; After starting a recipe, list its components with this. A component\n; declares a single item that is used, and the the tiers and counts of that\n; item for each tier of the recipe_item that's being made.\n; The list on the left side is the tiers, the list on the right is the counts.\n;\n; For instance:\n; recipe(white_producer)\n; component(2 3 5 7 9, screw, 2 4 4 4 4)\n; Means that a T1 white_producer needs 2 T2 screws, a T2 white_producer needs\n; 4 T3 screws, T3 requires 4 T5 screws, etc.\n; \n; There are additional space-saving wrinkles. Instead of repeating a number\n; for every tier or every count when it's always the same, you can list it\n; once. This is all-or-nothing: It must be exactly once, or listed once for\n; every tier in the recipe.\n; Also, in the tier list you can use numbers less than 1. This means that\n; the position in the list will be added to the number. I.e. a value of\n; 0 becomes equal to \"current_tier\", -1 becomes \"current_tier - 1\", etc.\n#component(tiers, item, counts) {lua(\\\n return factory.component([[{tiers}]],[[{item}]],[[{counts}]]);\\\n)}\n\n; For items that are made in machines, use this macro instead. This defines\n; the needed recipe automatically. The third argument is the \"multiplier\",\n; which says how many multiples of the item are made from a single source\n; input.\n; It is assumed that the production happens for all tiers of the item,\n; from the same source tier; things like mixing or boiling dust aren't handled\n; through this system. (Boiling isn't handled at all.)\n#produce(item, source, mult, machine) {lua(\\\n return factory.produce(\\\n [[{item}]],\\\n string.gsub([[{source}]], \" \", \"\"),\\\n {mult},\\\n string.gsub([[{machine}]], \" \", \"\")\\\n );\\\n)}\n\n; ===== BEGIN RECIPES =====\n\n{recipe(town_producer)}\n{component(2 3 5 7 9, screw, 2 4 4 4 4)}\n{component(2 0 0 0 0, plate, 2 0 0 0 0)}\n{component(0 3 5 7 9, board, 0 2 2 2 6)}\n{component(-1, chip, 0 2 2 2 4)}\n{component(-1, town_producer, 1)}\n\n{recipe(factory_producer)}\n{component(1 3 5 7 9, wire, 1 4 4 4 4)}\n{component(2 0 0 0 0, screw, 1 0 0 0 0)}\n{component(0 3 5 7 9, board, 0 1 1 1 3)}\n{component(2 3 5 0 0, plate, 2 2 2 0 0)}\n{component(0 0 0 7 9, dense_plate, 0 0 0 2 4)}\n{component(1 1 2 3 4, chip, 2 1 1 1 3)}\n{component(-1, factory_producer, 1)}\n\n{recipe(mine_producer)}\n{component(2 3 5 7 9, screw, 2 2 2 4 4)}\n{component(1 2 4 6 8, wire, 2 3 2 5 5)}\n{component(2 0 5 7 9, plate, 1 0 1 2 2)}\n{component(0 3 5 7 9, dense_plate, 0 2 2 2 2)}\n{component(1 1 2 3 4, chip, 1)}\n{component(-1, mine_producer, 1)}\n\n{recipe(workshop_producer)}\n{component(0 2 4 6 8, wire, 0 4 2 8 8)}\n{component(1 3 5 7 9, wire, 4 2 2 2 2)}\n{component(2 3 5 7 9, plate, 1 2 2 2 2)}\n{component(1 0 2 3 4, chip, 1 0 2 2 2)}\n{component(-1, workshop_producer, 1)}\n\n{recipe(construction_firm_producer)}\n{component(2 4 6 8 10, rod, 3 4 10 10 10)}\n{component(2 4 6 8 10, plate, 2)}\n{component(1 1 2 3 4, chip, 1 2 2 2 2)}\n{component(-1, construction_firm_producer, 1)}\n\n{recipe(laboratory_producer)}\n{component(0 3 5 7 9, pipe, 0 3 5 10 14)}\n{component(2 4 6 8 10, motor, 1)}\n{component(1 3 5 7 9, dense_plate, 2 2 4 4 6)}\n{component(0, chip, 3 2 4 4 6)}\n{component(-1, laboratory_producer, 1)}\n\n{recipe(headquarters_producer)}\n{component(1 3 5 7 9, wire, 2 4 8 8 12)}\n{component(2 4 6 8 10, motor, 1 2 2 2 2)}\n{component(0, chip, 3 2 4 4 6)}\n{component(-1, headquarters_producer, 1)}\n\n{recipe(powerplant_producer)}\n{component(1 3 5 7 9, insul_cable, 2 2 4 4 6)}\n{component(2 4 6 8 10, motor, 1)}\n{component(0 3 5 7 9, block, 0 3 5 5 14)}\n{component(0, chip, 3 2 4 4 6)}\n{component(-1, powerplant_producer, 1)}\n\n{recipe(arcade_producer)}\n{component(2 4 6 8 9, insul_cable, 4 6 6 6 6)}\n{component(2 4 6 8 10, pipe, 4 4 4 4 8)}\n{component(0, chip, 4 4 4 4 6)}\n{component(-1, arcade_producer, 1)}\n\n{recipe(tradingpost_producer)}\n{component(2 4 6 8 10, ring, 6 8 8 8 12)}\n{component(2 4 6 8 10, plate, 4)}\n{component(0, chip, 2 2 2 2 4)}\n{component(-1, tradingpost_producer, 1)}\n\n{recipe(shipyard_producer)}\n{component(2 4 6 8 10, insul_cable, 6 8 8 8 12)}\n{component(1 3 5 7 9, block, 4)}\n{component(0, chip, 2 2 2 2 4)}\n{component(-1, shipyard_producer, 1)}\n\n{recipe(museum_producer)}\n{component(3 5 7 9 10, insul_cable, 6 7 7 7 9)}\n{component(2 4 6 8 10, block, 4 5 5 5 7)}\n{component(0, chip, 2 2 2 2 4)}\n{component(-1, museum_producer, 1)}\n\n{recipe(statue_of_cubos_producer)}\n{component(1 3 5 7 9, dense_block, 4 5 5 5 7)}\n{component(2 4 6 8 10, motor, 2)}\n{component(2 4 6 8 10, pipe, 2)}\n{component(2 4 6 8 10, pump, 2 3 3 3 5)}\n{component(0, chip, 2 2 2 2 4)}\n{component(-1, statue_of_cubos_producer, 1)}\n\n{recipe(gem_producer)}\n{component(5, chip, 10)}\n{component(4, chip, 10)}\n{component(10, insul_cable, 2)}\n{component(1, dense_block, 1)}\n{component(2, dense_block, 1)}\n{component(3, dense_block, 1)}\n{component(4, dense_block, 1)}\n{component(5, dense_block, 1)}\n{component(6, dense_block, 1)}\n{component(7, dense_block, 1)}\n{component(8, dense_block, 1)}\n{component(9, dense_block, 1)}\n{component(10, dense_block, 1)}\n\n{recipe(exotic_producer)}\n{component(5, chip, 10)}\n{component(10, insul_cable, 2)}\n{component(10, dense_block, 10)}\n{component(10, assembly, 1)}\n{component(10, boiler, 1)}\n{component(10, crusher, 1)}\n{component(10, cutter, 1)}\n{component(10, mixer, 1)}\n{component(10, oven, 1)}\n{component(10, presser, 1)}\n{component(10, refiner, 1)}\n{component(10, shaper, 1)}\n{component(10, belt, 1)}\n\n{recipe(acceleration_booster)}\n{component(2 3 5, chip, 1 4 6)}\n{component(4, plate, 4 0 0)}\n{component(1, rainbow_plate, 0 2 4)}\n{component(4 8 0, circuit, 4 8 0)}\n{component(10, dense_block, 0 0 12)}\n{component(1, void_essence, 0 0 4)}\n{component(-1, acceleration_booster, 2)}\n\n{recipe(machine_booster)}\n{component(2 3 5, chip, 2)}\n{component(4 7 10, pipe, 4)}\n{component(4, wire, 2 0 0)}\n{component(0 7 10, dense_block, 0 2 2)}\n{component(8, dense_plate, 0 6 0)}\n{component(1, rainbow_plate, 0 0 4)}\n{component(1, void_essence, 0 0 2)}\n{component(4, circuit, 1 0 0)}\n{component(-1, machine_booster, 1)}\n\n{recipe(production_booster)}\n{component(1 3 0, chip, 4 4 0)}\n{component(2 7 0, plate, 4 6 0)}\n{component(2 7 10, circuit, 1 4 4)}\n{component(10, dense_block, 0 0 12)}\n{component(1, void_essence, 0 0 4)}\n{component(-1, production_booster, 0 1 4)}\n\n{recipe(resource_booster)}\n{component(3, plate, 6 0 0)}\n{component(1, rainbow_plate, 0 0 2)}\n{component(1 3 4, chip, 2 2 6)}\n{component(4 8 0, circuit, 1 2 0)}\n{component(0 7 10, block, 0 4 4)}\n{component(1, void_essence, 0 0 1)}\n{component(-1, resource_booster, 0 1 2)}\n\n{recipe(pumpkin_producer)}\n{component(0, carved_pumpkin, 6)}\n{component(0, anti_pumpkin, 6)}\n{component(0, pumpkin_plate, 20)}\n\n{recipe(oven)}\n{component(0, plate, 4 6 8 8 8 8 8 8 8 8)}\n{component(0, insul_cable, 2 2 3 3 3 4 4 4 4 4)}\n{component(0, block, 0 0 0 0 0 2 2 2 2 2)}\n{component(-1, oven, 1)}\n\n{recipe(presser)}\n{component(1, hammer, 2 0 0 0 0 0 0 0 0 0)}\n{component(0, plate, 4 5 7 7 7 9 9 9 9 9)}\n{component(0, wire, 1 1 2 2 2 3 3 3 3 3)}\n{component(0, block, 0 0 0 0 4 5 5 5 5 5)}\n{component(1 1 1 2 2 2 3 3 4 4, chip, 2)}\n{component(-1, presser, 1)}\n\n{recipe(assembly)}\n{component(0, pipe, 1 1 1 1 1 2 2 2 2 2)}\n{component(0, dense_plate, 6 5 8 8 8 10 10 10 12 12)}\n{component(0, motor, 1 1 1 1 1 1 1 1 2 2)}\n{component(1 1 1 2 2 2 3 3 4 4, chip, 1 1 1 1 1 1 1 1 1 2)}\n{component(-1, assembly, 1)}\n\n{recipe(refiner)}\n{component(0, block, 0 0 0 0 0 0 5 5 5 7)}\n{component(0, dense_plate, 4)}\n{component(0, motor, 1 1 1 1 2 2 2 2 2 4)}\n{component(0, ring, 1 2 2 2 3 3 3 3 3 5)}\n{component(0, pump, 1 2 2 2 3 3 3 3 3 5)}\n{component(1 1 2 2 2 2 3 4 4 5, chip, 2)}\n{component(-1, refiner, 1)}\n\n{recipe(mixer)}\n{component(0, dense_plate, 5 4 4 5 5 6 6 6 6 6)}\n{component(0, motor, 2)}\n{component(0, pump, 1 1 1 2 2 3 3 3 3 3)}\n{component(1 1 1 2 2 2 2 3 4 4, chip, 1 1 1 2 2 3 3 3 3 3)}\n{component(-1, mixer, 1)}\n\n{recipe(crusher)}\n{component(0, dense_plate, 7 8 8 8 8 8 8 9 9 9)}\n{component(0, motor, 1 1 1 1 1 1 1 2 2 2)}\n{component(1 1 1 2 2 2 2 2 4 4, chip, 1 2 2 2 2 2 2 3 3 3)}\n{component(-1, crusher, 1)}\n\n{recipe(belt)}\n{component(1, rubber, 3 4 4 4 4 0 0 0 0 0)}\n{component(1, rubber_plate, 0 0 0 0 0 4 5 5 5 5)}\n{component(0, motor, 3 3 3 3 3 3 4 4 4 4)}\n{component(0, insul_cable, 3 4 4 4 4 4 5 5 5 5)}\n{component(0 0 0 1 2 2 3 3 4 4, chip, 0 0 0 4 4 4 5 5 5 5)}\n{component(-1, belt, 1)}\n\n{recipe(cutter)}\n{component(0, plate, 2 2 2 2 2 2 2 0 0 0)}\n{component(0, dense_plate, 3 3 3 3 3 4 4 2 2 2)}\n{component(0, block, 0 0 0 0 0 0 0 4 4 6)}\n{component(0, motor, 3 4 4 4 4 5 5 5 5 7)}\n{component(-1, cutter, 1)}\n\n{recipe(shaper)}\n{component(0, plate, 4 4 4 4 4 4 4 4 4 0)}\n{component(0, dense_plate, 0 0 0 0 0 0 0 0 0 4)}\n{component(0, screw, 1 2 2 2 2 3 3 3 3 3)}\n{component(0, block, 1 2 2 2 2 3 3 3 3 0)}\n{component(0, dense_block, 0 0 0 0 0 0 0 0 0 5)}\n{component(0, motor, 2)}\n{component(0, insul_cable, 1 1 1 1 1 2 2 2 2 4)}\n{component(-1, shaper, 1)}\n\n{recipe(boiler)}\n{component(0, wire, 2 2 2 3 3 3 3 3 4 5)}\n{component(0, dense_plate, 2 2 2 3 3 3 3 3 4 5)}\n{component(0, block, 4 7 7 8 8 8 8 8 9 10)}\n{component(0, motor, 1 1 1 2 2 2 2 2 3 4)}\n{component(0, screw, 2)}\n{component(0, pump, 1)}\n{component(-1, boiler, 1)}\n\n{recipe(rainbow_dust)}\n{component(1, dust, 1)}\n{component(2, dust, 1)}\n{component(3, dust, 1)}\n{component(4, dust, 1)}\n{component(5, dust, 1)}\n{component(6, dust, 1)}\n{component(7, dust, 1)}\n{component(8, dust, 1)}\n{component(9, dust, 1)}\n{component(10, dust, 1)}\n\n{recipe(chip)}\n{component(1 3 5 7 9, circuit, 2)}\n{component(2 4 6 8 10, circuit, 2 4 4 2 2)}\n{component(1 3 5 7 9, board, 1 4 4 6 8)}\n{component(2 4 6 8 10, board, 1 2 2 6 8)}\n{component(-1, chip, 0 4 8 12 12)}\n\n{recipe(insul_cable)}\n{component(0, cable, 1 1 1 2 3 4 5 10 12 16)}\n{component(1, rubber, 1 2 0 0 0 0 0 0 0 0)}\n{component(1, rubber_plate, 0 0 2 4 6 8 10 10 12 16)}\n\n{recipe(stacked_plate)}\n{component(0, plate, 9)}\n\n{recipe(stacked_pumpkin)}\n{component(0, pumpkin, 9)}\n\n{recipe(motor)}\n{component(0, plate, 4)}\n{component(0, screw, 1)}\n{component(0, rod, 2)}\n{component(0, wire, 1)}\n{component(1, rubber, 1)}\n\n{recipe(pump)}\n{component(0, plate, 2)}\n{component(0, motor, 1)}\n{component(0, ring, 2)}\n{component(1, rubber_plate, 4)}\n\n{recipe(hammer)}\n{component(2, ingot, 6)}\n{component(2, rod, 1)}\n\n{recipe(block)}\n{component(0, dense_plate, 8 8 8 8 8 8 12 12 12 12)}\n\n{recipe(rubber_sapling)}\n{component(0, rubber, 8)}\n{component(9, ore, 1)}\n\n{recipe(void_sapling)}\n{component(1, rainbow_dust, 8)}\n{component(1, rubber_sapling, 1)}\n\n{recipe(producers)}\n#recipe_producers(x) \\\n{component(0, town_producer, {x})}\\\n{component(0, factory_producer, {x})}\\\n{component(0, mine_producer, {x})}\\\n{component(0, workshop_producer, {x})}\\\n{component(0, construction_firm_producer, {x})}\\\n{component(0, laboratory_producer, {x})}\\\n{component(0, headquarters_producer, {x})}\\\n{component(0, powerplant_producer, {x})}\\\n{component(0, arcade_producer, {x})}\\\n{component(0, tradingpost_producer, {x})}\\\n{component(0, shipyard_producer, {x})}\\\n{component(0, museum_producer, {x})}\\\n{component(0, statue_of_cubos_producer, {x})}\n{recipe_producers(1)}\n\n{recipe(machines)}\n{component(0, oven, 1)}\n{component(0, presser, 1)}\n{component(0, assembly, 1)}\n{component(0, refiner, 1)}\n{component(0, mixer, 1)}\n{component(0, crusher, 1)}\n{component(0, belt, 1)}\n{component(0, cutter, 1)}\n{component(0, shaper, 1)}\n{component(0, boiler, 1)}\n\n{recipe(parts)}\n#recipe_parts \\\n{component(0, insul_cable, 1)}\\\n{component(0, stacked_plate, 1)}\\\n{component(0, motor, 1)}\\\n{component(0, pump, 1)}\\\n{component(1, hammer, 0 1 0 0 0 0 0 0 0 0)}\\\n{component(0, block, 1)}\\\n{component(0, ingot, 1)}\\\n{component(0, plate, 1)}\\\n{component(0, dense_plate, 1)}\\\n{component(0, dense_block, 1)}\\\n{component(0, pipe, 1)}\\\n{component(0, cable, 1)}\\\n{component(0, wire, 1)}\\\n{component(0, rod, 1)}\\\n{component(0, ring, 1)}\\\n{component(0, screw, 1)}\\\n{component(0, board, 1)}\\\n{component(0, circuit, 1)}\\\n{component(0, rubber_plate, 1 0 0 0 0 0 0 0 0 0)}\\\n{component(1, rubber_sapling, 0 0 0 0 0 0 0 0 1 0)}\n{recipe_parts}\n\n{recipe(chips)}\n{component(1, chip, 1)}\n{component(2, chip, 1)}\n{component(3, chip, 1)}\n{component(4, chip, 1)}\n{component(5, chip, 1)}\n\n{recipe(chip_and_prods)}\n{component(0, chip, 1)}\n{recipe_producers(1)}\n\n#recipe_machines_and_parts \\\n{component(0, oven, 1)}\\\n{component(1, presser, 0 1 0 0 0 0 0 0 0 0)}\\\n{component(0, presser, 0 1 1 1 1 1 1 1 1 1)}\\\n{component(0, assembly, 1)}\\\n{component(0, refiner, 1)}\\\n{component(0, mixer, 1)}\\\n{component(0, crusher, 1)}\\\n{component(0, belt, 1)}\\\n{component(0, cutter, 1)}\\\n{component(0, shaper, 1)}\\\n{component(0, boiler, 1)}\\\n{recipe_parts}\n\n{recipe(all)}\n{component(0, chip, 1 1 1 1 1 0 0 0 0 0)}\n{recipe_producers(1 1 1 1 1 0 0 0 0 0)}\n{recipe_machines_and_parts}\n\n; Machine-produced items\n{recipe(ingot)} ; Has special-case producing code\n{component(0, dust, 1)}\n\n{produce(rainbow_ingot, rainbow_dust, 1, oven)}\n\n{produce(plate, ingot, 1, presser)}\n{produce(rainbow_plate, rainbow_ingot, 1, presser)}\n{produce(dense_plate, stacked_plate, 1, presser)}\n{produce(rubber_plate, rubber, 1, presser)}\n{produce(pumpkin_plate, stacked_pumpkin, 1, presser)}\n\n{produce(dense_block, block, 1, boiler)}\n{produce(anti_pumpkin, pumpkin, 1, boiler)}\n\n{produce(rod, ingot, 2, shaper)}\n{produce(pipe, plate, 1, shaper)}\n{produce(ring, rod, 1, shaper)}\n\n{produce(cable, ingot, 2, refinery)}\n{produce(board, plate, 1, refinery)}\n{produce(wire, cable, 1, refinery)}\n\n{produce(screw, rod, 4, cutter)}\n{produce(carved_pumpkin, pumpkin, 1, cutter)}\n\n{produce(circuit, cable, 1, assembler)}\n\n; These recipes have special-case code to handle them, because of the\n; complexities involved with choosing between ore and lumps for making dust.\n{recipe(lump)}\n{recipe(dust)}\n\n; Terminal items, unable to be crafted.\n{recipe(ore)}\n{recipe(rubber)}\n{recipe(void_essence)}\n{recipe(pumpkin)}\n\n; ===== END RECIPES =====\n\n{lua(return factory.check_recipes())}\n"],["run_recipes",":import factory constants\n:import recipes\n\n:name {script_name(run_recipes)}\n\n; Debugging function, kept in case of future problems.\n#debug_dump_recipes {lua(\\\n acc = {};\\\n for i = 1, #factory.items do\\\n local item = factory.items[i];\\\n acc[#acc+1] = string.format(\"%02d %-18s [\", i, item.name);\\\n for j = 1, item.tier do\\\n if j ~= 1 then acc[#acc+1] = string.format(\"\\n%23s\", \"\") end\\\n acc[#acc+1] = \"(\";\\\n local recipe = factory.recipes[i * 10 + j - 1];\\\n for k = 1, #recipe do\\\n if k ~= 1 then acc[#acc+1] = \" \" end\\\n acc[#acc+1] = recipe[k];\\\n end\\\n acc[#acc+1] = \")\";\\\n end\\\n acc[#acc+1] = \"]\\n\";\\\n end\\\n return table.concat(acc);\\\n)}\n\n;{debug_dump_recipes}\n\n; Now that all the recipes are defined, we have to put them in a valid order.\n; We do this with a modified breadth-first-search, optimized around the\n; structure of our data.\n; Each item (which in this context is a type-tier pair, identified by\n; the formula type * 10 + tier) is sequentially checked against a graph\n; that is incrementally being formed. If all its recipe-items have already been\n; satisfied (or it has none), then it is also satisfied, and (as long\n; as it has recipe-items) it's put on a queue to be output. Otherwise, a count\n; is kept of how many unsatisfied recipes it has, and an entry is made in each\n; blocking recipe pointing back to this item.\n; After each item, the queue is processed. The head of the queue is popped\n; and gets the next sequential id; this is how the recipes get their order.\n; Also, any items blocked on it will have their tallies decremented by one.\n; If these go to zero, they are now satisfied and will be added to the queue\n; to be output, as well.\n{lua(\\\n local recipes_list = {};\\\n factory.recipes_list = recipes_list;\\\n local items = factory.items;\\\n local recipes = factory.recipes;\\\n local graph = {};\\\n local queue = {};\\\n for tier = 10, 1, -1 do\\\n for item_id = 1, #items do\\\n local item = items[item_id];\\\n local id = 10 * item_id + tier - 1;\\\n local recipe = recipes[id];\\\n if not recipe then\\\n goto continue;\\\n end\\\n local entry = graph[id];\\\n if not entry then\\\n entry = {};\\\n entry.blocking = {};\\\n graph[id] = entry;\\\n end\\\n local blockers = 0;\\\n if recipe then\\\n for i = 1, #recipe, 2 do\\\n local other = graph[recipe[i]];\\\n if not other then\\\n other = {};\\\n other.blocking = {};\\\n other.blockers = -1;\\\n graph[recipe[i]] = other;\\\n end\\\n if other.blockers ~= 0 then\\\n blockers = blockers + 1;\\\n other.blocking[#other.blocking + 1] = id;\\\n end\\\n end\\\n end\\\n entry.blockers = blockers;\\\n \\\n if blockers == 0 then\\\n queue[#queue + 1] = id;\\\n end\\\n \\\n local q_front = 1;\\\n while q_front <= #queue do\\\n id = queue[q_front];\\\n entry = graph[id];\\\n recipes_list[#recipes_list + 1] = id;\\\n for i = 1, #entry.blocking do\\\n local other = graph[entry.blocking[i]];\\\n other.blockers = other.blockers - 1;\\\n if other.blockers == 0 then;\\\n queue[#queue + 1] = entry.blocking[i];\\\n end;\\\n end\\\n q_front = q_front + 1\\\n end\\\n queue = {};\\\n ::continue::\\\n end\\\n end\\\n)}\n\n; Debugging function, kept in case of future problems.\n#debug_dump_recipes_list {lua(\\\n acc = {};\\\n for i = 1, #factory.recipes_list do\\\n local id = factory.recipes_list[i];\\\n acc[#acc+1] = string.format(\"%03d:\", id);\\\n local recipe = factory.recipes[id];\\\n for j = 1, #recipe do\\\n acc[#acc+1] = string.format(\" %3s\", recipe[j]);\\\n end\\\n acc[#acc+1] = \"\\n\";\\\n end\\\n return table.concat(acc);\\\n)}\n\n;{debug_dump_recipes_list}\n\n; Constructs the data table that is used to create loop_data. See below\n; for the format of this string. One difference is that in loop_data, \n; the previous item/current item index data is at the beginning and end of the\n; string. Here, that is actually only stored once, and the sub() read window is\n; expanded to overlap consequetive sections to pick up the previous item when\n; reading the next.\n#get_data(num_terms) {lua(\\\n local num_terms = {num_terms};\\\n local num_terms_2 = num_terms * 2;\\\n local recipe_limit = 0;\\\n local acc_main = {};\\\n local sub = string.sub;\\\n \\\n local multipliers = \"0123a b c d e f g h i j k l m n o p q r s t u v w x y z\";\\\n local mult_max = {};\\\n for i = 1, num_terms do\\\n mult_max[i] = 0;\\\n end\\\n acc_main[1] = [[\"___]];\\\n for i = 1, #factory.recipes_list do\\\n local id = factory.recipes_list[i];\\\n local recipe = factory.recipes[id];\\\n local item_id = id // 10;\\\n local limit = (#recipe - 1) // num_terms_2;\\\n if limit < 0 then limit = 0 end;\\\n for j = 1, (limit + 1) * num_terms_2, 2 do\\\n acc_main[#acc_main + 1] = (j < #recipe) and recipe[j] + 100 or \" \";\\\n local mod = (j // 2) % num_terms + 1;\\\n local mult = (j < #recipe) and recipe[j+1] * 4 or 0;\\\n if mult > mult_max[mod] then\\\n mult_max[mod] = mult;\\\n end\\\n acc_main[#acc_main + 1] = sub(multipliers, mult + 1, mult + 1);\\\n if mod == num_terms then\\\n acc_main[#acc_main + 1] = id + 100;\\\n recipe_limit = recipe_limit + 1;\\\n end\\\n end\\\n end\\\n \\\n factory.main_size = 4 * num_terms + 3;\\\n acc_main[#acc_main + 1] = '\"';\\\n factory.multipliers = {};\\\n factory.num_terms = num_terms;\\\n factory.recipe_limit = recipe_limit;\\\n for i = 1, num_terms do\\\n if mult_max[i] >= #multipliers then\\\n return \"Multiplier limit exceeded at \" .. i;\\\n end\\\n factory.multipliers[i] = sub(multipliers, 1, mult_max[i] + 1);\\\n end;\\\n return table.concat(acc_main);\\\n)}\n\n#lookup_item {lua(\\\n return factory.composite_string(\\\n string.format(\\\n [[s2i(sub(gsg({data_name}), %d * (i + 1), 2), -1) - 11]],\\\n factory.main_size\\\n ),\\\n nil,\\\n true --[[Get craft type info instead of tiers]]\\\n );\\\n)}\n\n; Returns the set of lookup/multiplier terms that will be added in to form\n; the base of the value. Most of the data for this is pre-comupted by\n; get_data().\n#recipe_terms {lua(\\\n acc = {};\\\n for i = 1, factory.num_terms do\\\n if i ~= 1 then\\\n acc[#acc + 1] = \" + \";\\\n end\\\n acc[#acc + 1] = string.format(\\\n [[max(0., ceil(gdg({queue_str} . sub(loop_data, %d, 3)) *\\\n i2d(index(\"%s\", sub(loop_data, %d, 1), 0)) * 0.25))]],\\\n factory.entry_size + 4 * i - 1,\\\n factory.multipliers[i],\\\n factory.entry_size + 4 * i + 2\\\n );\\\n end\\\n return table.concat(acc);\\\n)}\n\n; This macro is used to test the type of an item, in order to efficiently\n; disable the count. Item groups don't have a single real item associated,\n; but the count() still has to count something real to avoid a spurious log line.\n; Ore and lumps are also classified as groups so that they won't be counted,\n; because they're treated specially.\n#item_type sub(loop_data, {lua(return factory.entry_size - 1)}, 1)\n\n; These macros are used to test if the item is a dust. Dust gets its queue value\n; inflated by one, which has the effect of always ending up with 1 at the end.\n; (Although it doesn't prevent temporarily using all dust.)\n#recipe_item_trunc sub(loop_data, {lua(\\\n return factory.entry_size + factory.main_size;\\\n)}, 2)\n#item_trunc(item) \"{lua(return factory.item_names[\"{item}\"].id + 10)}\"\n\n#prev_item sub(loop_data, {lua(return factory.entry_size)}, 3)\n#recipe_item sub(loop_data, {lua(\\\n return factory.entry_size + factory.main_size;\\\n)}, 3)\n; The tier value is just the last digit of recipe_item\n#tier_value sub(loop_data, {lua(\\\n return factory.entry_size + factory.main_size + 2;\\\n)}, 1)\n#recipe_item_name sub(loop_data, 0, index(loop_data, \" \", 0))\n#recipe_limit {lua(return factory.recipe_limit)}\n\n:global int factory_target\n:global double factory_target_amount\n\n; We use the name of the data variable to pull double-duty as a\n; hider for all the craft-queue variables.\n; This macro gives a prettier name for the global data string.\n#data_name \"fdata\"\n\n:local int i\n:local string loop_data\n\n; Uncomment this for debugging. It needs to be before the data-hiding block.\n; Also uncomment the line below.\n;gss(\"debug\", \"\")\n\ngss({data_name}, {get_data(4)})\nloop:\n\n; Evaluates to an expression that results in an string containing encoded data\n; for this recipe. The first entry_size-1 characters are the in-game item name,\n; space-padded. Then comes the item type info: 0 is a regular item, 1 is a\n; crafted item, and 2 is an item-group. After that is a series of 3-number\n; strings, each of which is a craft-queue index.\n; The first is the index for the previous item, which is used to determine if\n; this is a continuation from a previous line. The next `num_terms`\n; terms are index values for queue values to add. After each term is a single\n; character which is a multiplier value. Following that is an index for the\n; current item, which is used as the index to set, and possibly also as an\n; index to read from.\n;\n; All of this is pulled from reading the appropriate sections of \"data\", mostly\n; as-is. However, there is a secondary lookup for the item name.\nloop_data = {lookup_item} . sub(\\\n gsg({data_name}),\\\n i * {lua(return factory.main_size)},\\\n {lua(return factory.main_size + 3)}\\\n)\n\n; The core expression that does all the work. If this item is the target item,\n; then set the queue value to factory_target_amount - this ensures that the\n; target is always made, even if it already exists.\n; Otherwise, we set it to the sum of all of its recipe terms, minus the existing\n; count. This core value is the \"queue value\", and equals how many must be\n; crafted (if positive) or how many extra we have (if negative).\n; Since we are hardcoding the number of recipe terms that are handled in each\n; loop iteration to a small constant (4), there are additional wrinkles because\n; we may need to process the same item multiple times to get all the recipe\n; terms in. This means that if we're seeing the same item again, we add the\n; previous value of the variable and skip subtracting the count.\n; We also skip the count if the item is a group, since those don't have valid\n; items to count anyway.\n; In this way, we efficiently encode a sum that requires multiple passes.\nglobal.double.set({queue_str} . {recipe_item},\\\n if({recipe_item} == i2s(factory_target + 100),\\\n factory_target_amount,\\\n (\\\n if({prev_item} == {recipe_item}, global.double.get({queue_str} . {recipe_item}), 0.) +\\\n {recipe_terms} -\\\n if(\\\n {prev_item} == {recipe_item} || {item_type} == \"2\",\\\n 0.,\\\n if({recipe_item_trunc} == {item_trunc(dust)}, -1., 0.) + count({recipe_item_name}, index(\" 0123456789\", {tier_value}, 0))\\\n )\\\n )\\\n )\\\n)\ni = i + 1\n; Uncomment this line for debugging. It will show the quantities and ids of all\n; items that need to be produced. (The ids will be +100, because of internal reasons.)\n; You will also need to enable another line above.\n;gss(\"debug\", gsg(\"debug\") . if(gdg({queue_str} . {recipe_item}) > 0., \"
\" . {recipe_item} . \" \" . gdg({queue_str} . {recipe_item}), \"\"))\ngotoif(loop, i < {recipe_limit})\n\n; This used to be a global variable, but we don't need that anymore now that\n; production is algorithmic.\n:local int _tier\n\n_tier = 10\ntierloop:\n\n{add_queue(_tier, dust, max(0., ({get_queue(_tier, lump)} - if(_tier == 10, 0., count(\"lump\", min(9, _tier)))) * 4.0))}\n{add_queue(_tier, ore, max(0., min(count(\"ore\", _tier), ceil({get_queue(_tier, dust)} / 2.))))}\n\ngotoif(nolumps, _tier == 1 | count(\"ore\", _tier) + count(\"dust\", _tier) == 0.)\n{add_queue(-1 + _tier, lump, max(0., {get_queue(_tier, dust)} - {get_queue(_tier, ore)} * 2.))}\nnolumps:\n\n_tier -= 1\ngotoif(tierloop, _tier > 0)\n\n; If we're missing the resources needed to complete the recipe, set error.\ngss(\\\n {action},\\\n if(\\\n {get_queue(1, ore)} * 2. < {get_queue(1, dust)},\\\n \"Missing \" . ({get_queue(1, dust)} - {get_queue(1, ore)} * 2.) . \" T1 dust
and/or higher tier dust/ore!\",\\\n if(\\\n {get_queue(1, rubber)} > 0.,\\\n \"Missing \" . {get_queue(1, rubber)} . \" rubber!\",\\\n if(\\\n {get_queue(1, void_essence)} > 0.,\\\n \"Missing \" . {get_queue(1, void_essence)} . \" void essence!\",\\\n if(\\\n {get_queue(1, pumpkin)} > 0.,\\\n \"Missing \" . {get_queue(1, pumpkin)} . \" pumpkin(s)!\",\\\n \"\"\\\n )\\\n )\\\n )\\\n )\\\n)\n\n; This must come before the unset, to avoid ordering mishaps.\ngss(\"fhide2\", \"\")\n\n; If there's an error, unset target to cancel crafting and clean up future\n; iterations. (This leaves some mess behind if the target is changed to something\n; else without resetting AI, but it should be good enough.)\n{set_raw(factory_target, if(\\\n contains(gsg({action}), \"<\"),\\\n 0.,\\\n {get_raw(factory_target)}\\\n))}\n"],["produce",":import factory constants\n:import recipes\n\n:name {script_name(produce)}\n\n:local int i\n:local int machine_idx\n:local double previous_amount\n:local string machine_chunk\n:local string machine_name\n:local string produce_data\n:local string source_name_raw\n\nkey.{left}()\nkey.{right}()\n\nisopen(\"factory\")\n\n; If we were launched via key impulse, invoke the UI immediately to start turbo,\n; and set action to signal which key was pressed. Otherwise, we're being called\n; as a produce script.\nexecute(if(contains(impulse(), \"key.\"), \"{script_name(ui)}\", \"###badname###\"))\ngotoif(end, contains(impulse(), \"key.\"))\n\n; ================== BEGIN MACROS + LUA ==================\n; The data for producing is defined in a single string, stored in\n; produce_data. Each entry in the string is a \"produce_chunk\", defined\n; in the following format:\n; MULT SOURCE_CHAR DEST_ITEM\n; DEST_ITEM is a 3-byte number, denoting the raw queue value for the item.\n; (I.e. after the +100 addition.) SOURCE_CHAR is a single character that\n; is used for indexing a separate lookup table of the source item names.\n; (The source tier will be the same as the tier of DEST_ITEM.) The MULT\n; is a single number used when items produce multiple output copies.\n#produce_chunk_size 5\n\n; Here we precompute all our data tables. There are three tables:\n; produce_data, which stores the produce chunks described above.\n; item_names, which stores destination item names only, and\n; machine_data, which stores a combination of machine name and offset into produce_data.\n;\n; We also compute the character lookup table that is used to convert DEST_ITEM to/from\n; numbers for use with item_names.\n{lua(\\\n local format = string.format\\\n\\\n machine_chunk_size = nil\\\n machine_data = nil\\\n item_names = nil\\\n item_names_size = nil\\\n trans_table = \"0123456789abcdefghijklmnopqrstuvwxyz\"\\\n produce_data = nil\\\n\\\n local function pad_strings(arr)\\\n local max = 0\\\n for i = 1, #arr do\\\n max = max >= #arr[i] and max or #arr[i]\\\n end\\\n max = max + 1\\\n local fmt_str = format(\"%%%ds\", -max)\\\n local res = {}\\\n for i = 1, #arr do\\\n res[i] = format(fmt_str, arr[i])\\\n end\\\n return res, max\\\n end\\\n\\\n local machine_acc = {}\\\n local item_acc = {}\\\n local prod_acc = {}\\\n local prods_offset = 1\\\n local offsets = {}\\\n factory.prod_machines[#factory.prod_machines + 1] = \"end\"\\\n factory.prod_machines[\"end\"] = #factory.prods + 1\\\n for i = 1, #factory.prod_machines do\\\n local machine = factory.prod_machines[i]\\\n machine_acc[i] = machine\\\n local next_offset = factory.prod_machines[machine]\\\n for j = 1, 10 do\\\n for k = prods_offset, next_offset - 1 do\\\n local prod = factory.prods[k]\\\n if prod.source.tier < j then goto continue end\\\n local source_name = prod.source.game_name\\\n if not item_acc[source_name] then\\\n item_acc[#item_acc + 1] = source_name\\\n item_acc[source_name] = #item_acc\\\n end\\\n prod_acc[#prod_acc + 1] = prod.mult ..\\\n string.sub(trans_table, item_acc[source_name], item_acc[source_name]) ..\\\n prod.item.id * 10 + 100 + j - 1\\\n ::continue::\\\n end\\\n end\\\n offsets[i] = format(\"%03d\", #prod_acc * 5)\\\n prods_offset = next_offset\\\n end\\\n if #prod_acc * 5 > 999 then\\\n return \"Offset overflows three digits: \" .. table.concat(offsets, \" \")\\\n end\\\n machine_acc, machine_chunk_size = pad_strings(machine_acc)\\\n for i = 1, #machine_acc do\\\n machine_acc[i] = offsets[i] .. machine_acc[i]\\\n end\\\n\\\n machine_data = table.concat(machine_acc)\\\n machine_chunk_size = machine_chunk_size + 3\\\n item_acc, item_names_size = pad_strings(item_acc)\\\n item_names = table.concat(item_acc)\\\n trans_table = string.sub(trans_table, 1, #item_acc)\\\n produce_data = table.concat(prod_acc)\\\n\\\n--[[Debug functions, uncomment as needed]]\\\n--[[return table.concat(prod_acc, \",\")]]\\\n)}\n\n;{lua(return produce_data)}_\n;{lua(return machine_data)}\n;{lua(return item_names)}\n;{lua(return trans_table)}_\n\n; When it is loaded from produce_data, the value in produce_data is\n; processed from the raw produce_chunk to lookup the SOURCE_CHAR from the\n; secondary lookup table. This still needs extra processing to trim the\n; trailing spaces, which is why the source_name macro exists.\n#dest_offset 2\n#source_char_offset 1\n#mult_offset 0\n#get_source_tier index(\" 0123456789\", sub(produce_data, {dest_offset} + 2, 1), 0)\n#source_name sub(source_name_raw, 0, index(source_name_raw, \" \", 0))\n#mult s2d(sub(produce_data, {mult_offset}, 1), 0.)\n\n#next_offset s2i(sub(machine_chunk, {lua(return machine_chunk_size)}, 3), 0)\n\n; Raw access to the queue. This is even more direct than\n; get_raw, because we're using queue strings directly.\n#raw_dest sub(produce_data, {dest_offset}, 3)\n#get_dest global.double.get({queue_str} . {raw_dest})\n#set_dest(value) global.double.set({queue_str} . {raw_dest}, {value})\n; ================== END MACROS + LUA ==================\n\nexecutesync(\"{script_name(produce dust)}\")\nstop(\"{script_name(produce dust)}\")\n\nmachine_loop:\n; Setup machine data variables and set the produce index to the proper place.\n; We break this out into separate variables because we don't run through this\n; section as much (only once per machine, so ~8 times), and it makes the later\n; expressions more efficient. Also, we have lines to spare.\n; We get 3 extra characters after the machine chunk so that we have the next\n; offset available as well, which we use to determine when to finish the produce_loop\n; below and procede to the next machine.\nmachine_chunk = sub(\"{lua(return machine_data)}\", machine_idx, {lua(return machine_chunk_size)} + 3)\nmachine_name = sub(machine_chunk, 3, index(machine_chunk, \" \", 0) - 3)\ni = s2i(sub(machine_chunk, 0, 3), 0)\nmachine_idx += {lua(return machine_chunk_size)}\n\n; We need to setup the production data anyway, so we branch there. This is\n; one instruction less efficient than other methods, but it lets us\n; reuse a lot of code so it's worth it.\ngoto(if(\\\n contains(\"end\", machine_name),\\\n end,\\\n set_data\\\n))\n\nproduce_loop:\nsource_name_raw = sub(\\\n \"{lua(return item_names)}\",\\\n index(\\\n \"{lua(return trans_table)}\",\\\n sub(produce_data, {source_char_offset}, 1),\\\n 0\\\n ) * {lua(return item_names_size)},\\\n {lua(return item_names_size)}\\\n)\n\n; Uncomment this line for runtime production debugging.\n; You will also need to uncomment the line in run_recipes that unhides debug.\n;gss(\"debug\", gsg(\"debug\") . \"
\" . {source_name} . {get_source_tier} . \"_\" . min(ceil({get_dest} / {mult}), count({source_name}, {get_source_tier})) . machine_name)\n\n; Just try to produce. There is a wrinkle here not present when crafting:\n; we take the minimum with the source count, so that we can use items as\n; quickly as they become available. We only craft the entire quantity, to\n; avoid needlessly filling the inventory.\nproduce(\\\n {source_name},\\\n {get_source_tier},\\\n min(ceil({get_dest} / {mult}), count({source_name}, {get_source_tier})),\\\n machine_name\\\n)\n\n; We can blindly subtract the number of items in the machine, because we've\n; guaranteed via the guards on our loop that we exit as soon as the machine\n; is active. This means the item in there *must* be the correct one, if the\n; count is > 0.\n{set_dest({get_dest} - {mult} * machine.item.count(machine_name))}\n\nnext_item:\ni += {produce_chunk_size}\nset_data:\nproduce_data = sub(\"{lua(return produce_data)}\", i, {produce_chunk_size})\n\n; Go to the next machine if we're done with this set of machines, or the machine\n; has become active. Otherwise, either produce the next item, or skip it if\n; the quantity is <= 0.\ngoto(if(\\\n i >= {next_offset},\\\n machine_loop,\\\n if(\\\n active(machine_name),\\\n machine_loop,\\\n if(\\\n {get_dest} <= 0.,\\\n next_item,\\\n produce_loop\\\n )\\\n )\\\n))\n\nend:\ngss({action}, if(\\\n contains(impulse(), \"key.\"),\\\n if(\\\n contains(impulse(), \"key.{left}\"),\\\n \"1\",\\\n \"-1\"\\\n ),\\\n gsg({action})\\\n))\n"],["produce dust",":import factory constants\n\n:name {script_name(produce dust)}\n\n:local int _tier\n:local double previous_amount\n\n_tier = _tier + 1\nprevious_amount = count(\"ore\", _tier)\nproduce(\"ore\", _tier, {get_queue(_tier, ore)}, \"crusher\")\n{subtract_queue(_tier, dust, (previous_amount - count(\"ore\", _tier)) * 2.)}\n{subtract_queue(_tier, ore, previous_amount - count(\"ore\", _tier))}\n\ngotoif(notierupdust, _tier >= 10 || {get_queue(_tier, lump)} <= 0. || active(\"mixer\"))\ncraft(\"lump\", _tier, min(\\\n min(\\\n (count(\"dust\", _tier) - 1.) / 4.,\\\n count(\"dust\", _tier + 1)\\\n ),\\\n {get_queue(_tier, lump)} - count(\"lump\", _tier)\\\n))\nprevious_amount = count(\"lump\", _tier)\nproduce(\"lump\", _tier, min(previous_amount, {get_queue(_tier, lump)}), \"mixer\")\n{subtract_queue(_tier, dust, previous_amount - count(\"lump\", _tier))}\n{subtract_queue(_tier, lump, previous_amount - count(\"lump\", _tier))}\nnotierupdust:\n\ngotoif(noproduceingot, active(\"oven\") | (if(_tier == 10, 0., ({get_queue(_tier, lump)} - count(\"lump\", min(_tier, 9)))) * 4. > 2. * min(0., count(\"dust\", _tier) - ({get_queue(_tier, ingot)})) & count(\"dust\", _tier) < 2. * ({get_queue(_tier, ingot)})))\nprevious_amount = count(\"dust\", _tier)\nproduce(\"dust\", _tier, min(previous_amount - 1., {get_queue(_tier, ingot)}), \"oven\")\n{subtract_queue(_tier, ingot, previous_amount - count(\"dust\", _tier))}\nnoproduceingot:\n\ngoto(if(_tier >= 10, 99, 1))\n"],["craft",":import factory constants\n:import recipes\n\n:name {script_name(craft)}\n\n:local double previous_amount\n:local int i\n:local string itemdata\n:local string group_data\n\n:global int factory_target\n\nkey.{up}()\nkey.{down}()\n\nisopen(\"factory\")\n\n; If we were launched via key impulse, invoke the UI immediately to start turbo,\n; and set action to signal which key was pressed. Otherwise, we're being called\n; as a produce script.\nexecute(if(contains(impulse(), \"key.\"), \"{script_name(ui)}\", \"###badname###\"))\ngotoif(group_abort, contains(impulse(), \"key.\"))\n\n; Craft all the items.\ncraftitems_loop:\nitemdata = {lua(return factory.composite_string(\"i/10\", \"craft\"))}\n\ndo_craft:\n; Because items and tiers start at 1, but i is zero-based, we have to add 1\n; or (pre-multiplying, 10) in these expressions.\n; However, tier is zero-based inside our indexing representation, so we don't\n; add it there.\nprevious_amount = count({get_name}, i%10 + 1)\ncraft({get_name}, i%10 + 1, {get_raw(10 + i)})\n{set_raw(10 + i, {get_raw(10 + i)} - (count({get_name}, i%10 + 1) - previous_amount))}\ninc:\n; This skips over tiers that don't exist for the given item, by using the\n; tier data embedded in \"itemdata\".\ni = i + if(i2s(i%10) == {tiers_char}, 10 - i%10, 1)\ngoto(if(i%10 != 0,\\\n if({get_raw(10 + i)} > 0., do_craft, inc),\\\n if(i < {items_count} * 10, craftitems_loop, crafting_done)))\ncrafting_done:\n\n; Uncomment these lines for runtime item quantity debugging.\n; You will also need to uncomment the line in run_recipes that unhides debug.\n;i = 10\n;debug_loop:\n;gss(\"debug\", if(i == 10, \"\", gsg(\"debug\") . if({get_raw(i)} > 0., \"
\" . i . \" \" . {get_raw(i)}, \"\")))\n;i += 1\n;gotoif(debug_loop, i < {lua(return #factory.items)} * 10 + 10)\n\ngroup_data = \"{lua(\\\n acc = {}\\\n for i = 1, #factory.items do\\\n local item = factory.items[i];\\\n if item.craft_type ~= \"group\" then goto continue end\\\n for j = 1, item.tier do\\\n acc[#acc+1] = \"|\" .. (i * 10 + j + 99);\\\n local recipe = item.recipes[j];\\\n for k = 1, #recipe, 2 do\\\n acc[#acc+1] = recipe[k] + 100;\\\n end\\\n end\\\n ::continue::\\\n end\\\n acc[#acc+1] = \"|\";\\\n return table.concat(acc);\\\n)}\"\n\ni = index(group_data, \"|\" . (factory_target + 100), 0) + 1\ncheck_group_loop:\ni += 3\ngoto(if(\\\n i < 4, group_abort,\\\n if(\\\n sub(group_data, i, 1) == \"|\", group_complete,\\\n if(\\\n gdg({queue_str} . sub(group_data, i, 3)) > 0., group_abort,\\\n check_group_loop\\\n )\\\n )\\\n))\n\ngroup_complete:\n{set_raw(factory_target, 0.)}\ngroup_abort:\ngss({action}, if(\\\n contains(impulse(), \"key.\"),\\\n if(\\\n contains(impulse(), \"key.{up}\"),\\\n \"1\",\\\n \"-1\"\\\n ),\\\n gsg({action})\\\n))\n"],["init",":import factory constants\n\n:name {script_name(init)}\n\nwakeup()\nopen.factory()\n\nisopen(\"factory\")\n\n:global double factory_target_amount\n:global int factory_target\n\n:global int turbo.cycles\n:global int turbo.cycles.max\n\n:local double target_value\n\n; Run the UI, to display the current pending item.\nexecute(\"{script_name(ui)}\")\n\n; Initial dispatch on entering the factory to determine if we are resuming\n; a crafting operation or waiting to launch a new one. This is done outside\n; of turbo, so that we never invoke turbo if we don't need to.\ngotoif(wait_loop, {get_raw(factory_target)} == 0.)\n\n; The core factory cycle. We do each loop of factory production within\n; one call of nested turbo start/stop, which executes within one frame.\n;\n; This loop handles both calculating recipes via \"run_recipes\"\n; and crafting via \"craft\". This is done with conditional execution,\n; in order to save lines for future possible features.\nbegin_cycle:\nexecutesync(\"TE2.2:start\")\n\n; Here we up the number of cycles so that it will be enough to\n; calculate the recipes or produce items. We intentionally have a\n; frame break between the recipe calculation and the beginning of crafting\n; to prevent jarring lag on startup.\n; This formula ensures that we don't accidentally step on another script\n; that needs more cycles, and also that we get the full amount of cycles\n; even if something else started turbo before us.\nturbo.cycles.max = max(turbo.cycles.max, turbo.cycles + 4000)\n\n; We have to save this, because the value can change as a result of executing\n; later scripts.\ntarget_value = {get_raw(factory_target)}\n\n; All conditional execution in the loop is behind this condition. If it\n; is true, then we're here because of `ui`. Otherwise,\n; this is a regular crafting iteration.\n\n; There are extra checks for isopen(\"factory\") here. The main check is at the\n; bottom of the loop, but that happens before the end of the frame, so there will\n; be one more frame where we have exited the factory, but the loop still runs.\n; Normally, this wouldn't be an issue (all of the scripts will perform no actions\n; and leave the variables in the same state), but \"produce\" and \"craft\" have a\n; startup condition of isopen(\"factory\"), since they do double-duty as UI impulse\n; scripts as well.\n; So, we have to protect them, otherwise executesync() will hang when it hits\n; the false condition, leading to a stuck script.\nexecutesync(if(\\\n isopen(\"factory\"),\\\n if(target_value > 0., \"{script_name(produce)}\", \"{script_name(run_recipes)}\"),\\\n \"###badname###\"\\\n))\nstop(if(target_value > 0., \"{script_name(produce)}\", \"{script_name(run_recipes)}\"))\nexecutesync(if(\\\n target_value > 0. && isopen(\"factory\"),\\\n \"{script_name(craft)}\",\\\n \"###badname###\"\\\n))\nstop(if(target_value > 0., \"{script_name(craft)}\", \"###badname###\"))\n\n; Clear factory_target to indicate that crafting is done, if it is, in fact, done.\nfactory_target = if({get_raw(factory_target)} > 0., factory_target, 0)\n; Re-display the UI when crafting is done.\nexecute(if(factory_target == 0, \"{script_name(ui)}\", \"###badname###\"))\n\n; Clear this (unconditionally), to signal that we're not starting a new crafting\n; pass.\nfactory_target_amount = 0.\n\nexecutesync(\"TE2.2:stop\")\n\n; Here we either return to the next iteration of the production loop,\n; or stall on this instruction until we need to launch the factory.\n; Because there is always 1 extra cycle of turbo after \"TE2.2:stop,\" there\n; is enough time to execute this goto and have only a single frame break\n; before starting turbo again at the top of the loop.\nwait_loop:\ngotoif(\\\n if(max(factory_target_amount, {get_raw(factory_target)}) > 0.,\\\n begin_cycle, wait_loop\\\n ),\\\n isopen(\"factory\")\\\n)\n\n; Remove the UI status so it doesn't clutter the variables when we're outside the\n; factory.\n; This is safe to repeat, when turbo is looping at the end of the script.\ngss({status}, \"\")\n"],["ui",":import factory constants\n:import worker_storage_lib\n\n:name {script_name(ui)}\n\n:local double count\n:local int visible_tier\n:local int tier\n:local int category\n:local int item\n:local int cursor\n\n:global int turbo.cycles\n:global int turbo.register\n:global int factory_target\n:global double factory_target_amount\n\nkey.{start}()\n\nisopen(\"factory\")\n\n; Because of how turbo exec works, we can't launch turbo on the frame the script\n; starts by calling \"TE2.2:start\", if we are being called from another script.\n; We can only do it by changing turbo.register directly, saving a layer of\n; script execution.\n; (This is because of the relative positioning of TE.turbo vs our script;\n; usually we would be before, but when we are first launched our script is after.)\nturbo.register += 1\n\n; Use worker_storage_lib to find a worker_slot to use for permanent storage.\n:local int worker_slot\n{worker_lib_line_1([factory])}\n{worker_lib_line_2}\n\n; Split out the worker data into separate variables. We take advantage of this\n; time to also perform increment/decrement, since we can easily combine it in\n; this stage. We don't bother checking if we've got a valid slot, because if we\n; don't, we'll simply fail to parse and get the fallback value.\n\n#action_num s2i(gsg({action}), 0)\n; Is the u/d action valid for this position?\n; Using contains() instead of == and chained compares saves a lot of import space.\n#is_ud(pos) contains(impulse() . cursor, \":craft{pos}\")\n\ncursor = min(9, max(0,\\\n s2i(sub(worker.name(worker_slot), {len({worker_prefix})} + 11, 1), 0) +\\\n if(contains(impulse(), \":produce\"), {action_num}, 0)\\\n))\ncount = min(9999999., max(1.,\\\n s2d(sub(worker.name(worker_slot), {len({worker_prefix})}, 7), 1.) +\\\n if(contains(impulse(), \":craft\"), i2d({action_num} * 10^(cursor - 3)), 0.)\\\n))\n\n#num_categories {lua(return #factory.categories)}\ncategory = (\\\n s2i(sub(worker.name(worker_slot), {len({worker_prefix})} + 8, 1), 0) -\\\n if({is_ud(1)}, {action_num}, 0) +\\\n {num_categories}\\\n) % {num_categories}\n\n; Because math expressions are so expensive, in terms of import space, it's best\n; to just pre-compute tables of the next/previous item to go to for every item.\n; To avoid duplicating expressions, we also make a no-op table for the case where\n; we're not incrementing or decrementing.\n{lua(\\\n function factory.item_inc_table(inc)\\\n local acc = {}\\\n local default = string.format(\"%02d\", factory.categories[1].default - 1)\\\n for i = 1, #factory.categories do\\\n local cat = factory.categories[i]\\\n local cat_size = cat.last - cat.first + 1\\\n for j = cat.first, cat.last do\\\n acc[j] = string.format(\"%02d\", (j + inc - cat.first) % cat_size + cat.first - 1)\\\n end\\\n end\\\n for i = 1, #factory.items do\\\n acc[i] = acc[i] or default\\\n end\\\n return table.concat(acc)\\\n end\\\n)}\n#item_inc_table(inc) {lua(return factory.item_inc_table({inc}))}\n#category_defaults {lua(\\\n local acc = {}\\\n for i = 1, #factory.categories do\\\n acc[i] = string.format(\"%02d\", factory.categories[i].default - 1)\\\n end\\\n return table.concat(acc)\\\n)}\n\n; Items in this variable are zero-indexed, as opposed to their regular ids, which\n; start from 1.\n; We increment the items in the opposite direction from the action, because pressing\n; up (action 1) *increments* numerical values but *decrements* elements in an\n; alphabetically sorted list.\nitem = s2i(\\\n if({is_ud(1)},\\\n sub(\\\n \"{category_defaults}\",\\\n category * 2,\\\n 2\\\n ),\\\n sub(\\\n if({is_ud(0)},\\\n if(\\\n contains(gsg({action}), \"-1\"),\\\n \"{item_inc_table(1)}\",\\\n \"{item_inc_table(-1)}\"\\\n ),\\\n \"{item_inc_table(0)}\"\\\n ),\\\n s2i(\\\n sub(worker.name(worker_slot), {len({worker_prefix})} + 9, 2),\\\n {lua(return factory.categories[1].default - 1)}\\\n ) * 2,\\\n 2\\\n )\\\n ),\\\n 0\\\n)\n\n; The distinction between \"tier\" and \"visible_tier\" has to do with the fact that\n; items have different tier maximums. When a user is scrolling through different\n; items, if the \"tier\" is T10 (represented as \"9\" in our zero-indexed scheme),\n; then the \"visible_tier\" will change to fit the maximum tier for the item, while\n; the tier remains at T10. However, if the item is a producer (max of T5), and\n; the user moves the cursor to the tier field and presses \"W\", the attempt to\n; increment \"tier\" will fix it at a new value of T5 (4).\n#tier_data {lua(\\\n local acc = {};\\\n for i = 1, #factory.items do\\\n local item = factory.items[i];\\\n acc[i] = string.format(\"%d\", item.tier - 1);\\\n end\\\n return table.concat(acc)\\\n)}\n\n; Expression to load the saved tier data from the worker name, including\n; a default value when there is no data.\n#saved_worker_tier s2i(sub(worker.name(worker_slot), {len({worker_prefix})} + 7, 1), 0)\n\n; It makes the most sense to set visible_tier first. There is a complication\n; when adjusting the tier down; in this case, we reduce the maximum bounds by\n; one, so that (for instance) if the max tier is T5, and the current tier is T10,\n; we'll properly clip the visible_tier to T4 after subtracting 1 to get T9.\n; This type of adjustment isn't needed (or wanted) in the other direction, or\n; when the tier isn't changing.\nvisible_tier = max(0,\\\n min(s2i(sub(\"{tier_data}\", item, 1), 9) +\\\n if({is_ud(2)} && gsg({action}) == \"-1\", -1, 0),\\\n {saved_worker_tier} + if({is_ud(2)}, {action_num}, 0)\\\n )\\\n)\ntier = if({is_ud(2)}, visible_tier, {saved_worker_tier})\n\n; Finally, construct the visible name for use in the variable.\n{lua(\\\n local names = {}\\\n local name_starts = {}\\\n local name_sizes = {}\\\n local name_len = 0\\\n\\\n for i = 1, #factory.items do\\\n local name = factory.items[i].name\\\n names[i] = name\\\n name_starts[i] = string.format(\"%03d\", name_len)\\\n name_len = name_len + #name\\\n name_sizes[i] = string.format(\"%02d\", #name)\\\n end\\\n factory.name_data = table.concat(names)\\\n factory.name_starts = table.concat(name_starts)\\\n factory.name_sizes = table.concat(name_sizes)\\\n factory.items_count = #factory.items\\\n\\\n names = {}\\\n for i = 1, #factory.categories do\\\n names[i] = factory.categories[i].name\\\n end\\\n factory.category_names = table.concat(names)\\\n)}\n#name_data {lua(return factory.name_data)}\n#name_starts {lua(return factory.name_starts)}\n#name_sizes {lua(return factory.name_sizes)}\n#category_names {lua(return factory.category_names)}\n\n; Set this variable in order to begin a variable-hiding block.\n; It looks like a no-op, but really we're ensuring that it has a slot in the\n; globals table, even if its value empty - in this case, what we care about\n; is the *name*, which starts with \"\".\n; Do this *after* all the other work, to give turbo register time to\n; set its variables, in case we're doing this right at startup.\ngss({action}, gsg({action}))\n\n; Do conditional stuff, depending on if we're launching the factory.\n; We set all these variables always, so that they'll have a consistent order,\n; and also to save lines instead of jumping over this block.\n\n#valid_start contains(factory_target . impulse(), \"0key.\")\n; This (maybe) launches the factory. We need to do it soon enough to not\n; interrupt turbo, see the comment on \"TE2.2:stop\", below.\n{lua(\\\n for i = 1, #factory.categories do\\\n if factory.categories[i].name == \"grup\" then\\\n grup_cat = i - 1;\\\n return;\\\n end\\\n end\\\n return \"Couldn't find grup\"\\\n)}\nfactory_target_amount = if(\\\n {valid_start},\\\n count,\\\n factory_target_amount\\\n)\n\n; Only set factory_target if it's zero, which indicates that the factory is idle.\n; This prevents repeated keypresses from messing things up.\nfactory_target = if({valid_start}, (item + 1) * 10 + visible_tier, factory_target)\n\n; Construct the status line. There's a *lot* that goes into this.\n;\n; For starters, we fake the variable - it's not really \"make\", but rather a\n; totally different variable that's less likely to collide. The true variable name\n; is never seen, because it's still part of the block. We put our fake\n; variable name after. We do all this so that we can \"unset\" the variable by\n; just changing its value - this way, it's not truly unset, and we don't have\n; issues with variable ordering as a result.\n;\n; This also means we can change the variable name to something else, like\n; \"error\", if we need to, all without actually creating a new variable or\n; changing ordering.\n;\n; All the clauses are dynamically created on the fly here. We also create the\n; \"cursor\" by highlighting a specific part of the result in green. This involves\n; a great deal more complexity.\n#curs_col 2f4\n; For later - scanning rework\n;count_string if(category == {lua(return grup_cat)}, \" -SCAN-\", d2s(10000000. + count))\n#count_string d2s(10000000. + count)\n\ngss({status}, if(\\\n worker_slot == 200,\\\n \"error=No available workers!\",\\\n if(\\\n turbo.cycles == 0,\\\n \"error=Turbo exec is not working\",\\\n \"make=\" .\\\n sub({count_string}, 1, 9 - cursor) .\\\n \"\".\\\n sub({count_string}, 10 - cursor, 1) .\\\n \"\".\\\n sub({count_string}, 11 - cursor, 10) .\\\n \"xT\", \"fff>T\") .\\\n (visible_tier + 1) .\\\n if(cursor == 1, \" \", \" \") .\\\n sub(\"{category_names}\", category * 4, 4) .\\\n if(cursor == 0, \" ...
\", \" ...
\") .\\\n sub(\"{name_data}\", s2i(sub(\"{name_starts}\", item * 3, 3), 0),\\\n s2i(sub(\"{name_sizes}\", item * 2, 2), 0)) .\\\n \"
\" . if(\\\n contains(impulse() . gsg({action}), \":init<\"),\\\n gsg({action}),\\\n if(\\\n factory_target == 0,\\\n \"{up}{left}{down}{right} moves, {start} crafts \",\\\n \"Crafting...\"\\\n )\\\n )\\\n )\\\n))\n\n; Pause/unpause a dissolve worker, if it occupies our data slot.\n; If it's a different type of worker, leave it alone by dummying out the name.\nworker.pauseName(\\\n if(\\\n contains(worker.task(worker.name(worker_slot)), \"factory.dissolveManagement\"),\\\n worker.name(worker_slot),\\\n \"![@#nosuchtask#@]!\"\\\n ),\\\n contains(gsg({status}), \"Crafting\")\\\n)\n\n; \"init\" will take over as soon as factory_target_amount gets set 4 lines\n; above; it will call its own \"TE2.2:start\" and thus prevent the frame from\n; ending before this line takes effect.\n; We need two cycles in-between to have a seamless transition.\n; We change the variable directly, instead of calling \"TE2.2:stop\", for parity\n; with the way we start: This avoids some edge cases when turbo is incorrectly\n; installed. It also is fine in this case, because we don't need to wait for\n; the end of the frame.\nturbo.register -= 1\n\n; Setting the data back in the worker is moved way down to the bottom, to function\n; as the \"filler\" action that can be repeated while the script is waiting for\n; the frame to end. We can't use of the actions that set global variables for this,\n; because they may need to be modified later in the frame, and thus setting them\n; here in a loop would overwrite the value.\nworker.setName(if(worker_slot < 100, worker_slot, worker_slot - 100),\\\n \"{worker_prefix}\" . sub(d2s(10000000. + count), 1, 7) . tier . category .\\\n sub(i2s(100 + item), 1, 2) . cursor\\\n)\n"]]}}
+{"workspaces":{"D0S.Factory":[["factory constants",":import factory macros\n\n#version v3.2.3\n\n; Everything in the list below is a valid target for automation, and can be\n; assigned to the \"target_type\" variable in \"lanuch factory craft\".\n; For example, \"make(oven, 10, \"machine.oven\")\" indicates that \"oven\" is a valid\n; item, usable from tiers 1-10, with the internal game name of \"machine.oven\"\n; (which you don't need to worry about.)\n; Thus you can enter \"oven\" in the box in the in-game editor (without the quotes).\n\n; If you add or change this list, mind this constraint:\n; * All the crafted items have to come before the rest of the items.\n; * All the item-groups must be in a single block.\n; * Prefer keeping things in alphabetical order within categories, because\n; they are ordered the same way when presented in-game.\n\n; ===== VALID IDS =====\n\n; *** To reiterate, just use the name part (i.e. \"white_producer\"), not the whole thing! ***\n\n; ----- CRAFTED ITEMS -----\n\n; Producers, named by building, tiers 1-5\n{make(arcade_producer, 5, \"producer.arcade\")}\n{make(construction_firm_producer, 5, \"producer.constructionFirm\")}\n{make(exotic_producer, 1, \"producer.exoticgems\")}\n{make(factory_producer, 5, \"producer.factory\")}\n{make(gem_producer, 1, \"producer.gems\")}\n{make(headquarters_producer, 5, \"producer.headquarters\")}\n{make(laboratory_producer, 5, \"producer.laboratory\")}\n{make(mine_producer, 5, \"producer.mine\")}\n{make(museum_producer, 5, \"producer.museum\")}\n{make(powerplant_producer, 5, \"producer.powerplant\")}\n{make(pumpkin_producer, 1, \"pumpkin.producer\")}\n{make(shipyard_producer, 5, \"producer.shipyard\")}\n{make(statue_of_cubos_producer, 5, \"producer.statueofcubos\")}\n{make(town_producer, 5, \"producer.town\")}\n{make(tradingpost_producer, 5, \"producer.tradingpost\")}\n{make(workshop_producer, 5, \"producer.workshop\")}\n\n; Boosters, tiers 1-3\n{make(acceleration_booster, 3, \"booster.acceleration\")}\n{make(machine_booster, 3, \"booster.machines\")}\n{make(production_booster, 3, \"booster.production.regular\")}\n{make(resource_booster, 3, \"booster.resource.drops\")}\n\n{category(prod, town_producer, arcade_producer, resource_booster)}\n\n; Machines, tiers 1-10\n{make(assembly, 10, \"machine.assembler\")}\n{make(belt, 10, \"machine.transportbelt\")}\n{make(boiler, 10, \"machine.boiler\")}\n{make(crusher, 10, \"machine.crusher\")}\n{make(cutter, 10, \"machine.cutter\")}\n{make(mixer, 10, \"machine.mixer\")}\n{make(oven, 10, \"machine.oven\")}\n{make(presser, 10, \"machine.presser\")}\n{make(refiner, 10, \"machine.refinery\")}\n{make(shaper, 10, \"machine.shaper\")}\n\n{category(mach, belt, assembly, shaper)}\n\n; Various crafted parts\n{make(block, 10, \"block\")}\n{make(chip, 5, \"chip\")}\n{make(hammer, 1, \"hammer\")}\n{make(insul_cable, 10, \"cable.insulated\")}\n{make(motor, 10, \"motor\")}\n{make(pump, 10, \"pump\")}\n{make(rainbow_dust, 1, \"dust.rainbow\")}\n{make(rubber_sapling, 1, \"sapling.rubber\")}\n{make(stacked_plate, 10, \"plate.stack\")}\n{make(stacked_pumpkin, 1, \"pumpkin.stack\")}\n{make(void_sapling, 1, \"sapling.void\")}\n\n{category(crft, chip, block, void_sapling)}\n\n; Transforms ore into dust, tiers 1-10\n; Because of an implementation detail, this must come before any\n; scannable items.\n; This doesn't show up in any category in the UI.\n{special(ore, 10, \"ore\")}\n\n; ----- SCANNABLE ITEM GROUPS -----\n; These are not real items, but rather groups of items that will be made together\n; if you select one of these names. They are meant for use with the Crafter, to\n; crank out sets of items to scan quickly. You will want to set the quantity to 1000.\n\n; Notes on using item groups:\n; * SCAN YOUR ORES FIRST! The factory can and will consume your ore to make stuff,\n; and it takes a long time to get 1000 T10 ore.\n; * The quantity works a little differently than normal items. Instead of making\n; 1000 items each time, running it again will top off everything to be\n; *at* the quantity of 1000 items.\n; * If you craft a higher tier after a lower tier, it will consume the results\n; of the first craft to make the higher tier. Scan all the items first, before\n; moving on to the next tier!\n; * None of these groups include Gem Producers or Exotic Producers. They are too\n; expensive in comparison to other things, craft them on your own when you\n; judge the time is right.\n\n; Makes *everything* (expect special producers and lumps) of the given tier.\n; This is the combination of \"chips_and_prods\", \"machines\", and \"parts\".\n; Requires x10 and high processing speed to have a hope of completing in a\n; reasonable amount of time for T5 and T10.\n; You need Quantum Warehouse to have enough space for this at higher tiers!\n{group(all, 10)}\n\n; All the tiers of chips. Warning: Expensive. Input this as tier 1.\n{group(chips, 1)}\n\n; Producers + the chip of the corresponding tier. This is everything that\n; exists in tier range 1-5, for convenience.\n{group(chip_and_prods, 5)}\n\n; All machines. Tiers 1-10\n{group(machines, 10)}\n\n; All ingredients and parts. Tiers 1-10. Doesn't include ore (scan that before\n; starting) or lumps (due to technical limitations). Lumps aren't required\n; for anything currently though.\n; Includes rubber plates at tier 1 and hammers at tier *2*.\n; Rubber trees are at tier 9 because of their ore.\n{group(parts, 10)}\n\n; All producers, except for special producers. Tiers 1-5\n{group(producers, 5)}\n\n{category(grup, all, all, producers)}\n\n; ----- INGREDIENTS AND PRODUCED PARTS -----\n\n; Parts, tiers 1-10\n{item(anti_pumpkin, 1, \"pumpkin.anti\")}\n{item(board, 10, \"plate.circuit\")}\n{item(cable, 10, \"cable\")}\n{item(carved_pumpkin, 1, \"pumpkin.carved\")}\n{item(circuit, 10, \"circuit\")}\n{item(dense_block, 10, \"block.dense\")}\n{item(dense_plate, 10, \"plate.dense\")}\n{item(ingot, 10, \"ingot\")}\n{item(pipe, 10, \"pipe\")}\n{item(plate, 10, \"plate\")}\n{item(pumpkin_plate, 1, \"pumpkin.plate\")}\n{item(rainbow_ingot, 1, \"ingot.rainbow\")}\n{item(rainbow_plate, 1, \"plate.rainbow\")}\n{item(ring, 10, \"ring\")}\n{item(rod, 10, \"rod\")}\n{item(rubber_plate, 1, \"plate.rubber\")}\n{item(screw, 10, \"screw\")}\n{item(wire, 10, \"wire\")}\n\n{category(part, circuit, anti_pumpkin, wire)}\n\n;Tries to make dust from ores and lower-tier dusts, tiers 1-10\n; Doesn't appear in the UI\n{item(dust, 10, \"dust\")}\n\n; Tiers up dust, tiers 1-9\n; These are ore lumps, plus putting them into the mixer.\n; There is no \"only make ore lumps, without mixing them\" option right now.\n; Doesn't appear in the UI\n{special(lump, 9, \"lump\")}\n\n; Can only be made from saplings\n; Doesn't appear in the UI\n{item(void_essence, 1, \"essence.void\")}\n\n; Can only be gotten through the Halloween event\n; Doesn't appear in the UI\n{item(pumpkin, 1, \"pumpkin\")}\n\n; Rubber, tier 1 only\n; Doesn't appear in the UI\n{item(rubber, 1, \"rubber\")}\n\n; ===== END OF VALID IDS =====\n"],["factory macros","; Infrastructure for producing all the constants in the factory package.\n; Since this is :imported, it can't produce any instructions.\n\n#script_name(name) D0S.factory {version}:{name}\n\n; Key assignments. You can edit these here, or you can edit them later in the\n; generated script (but you will have more places to modify).\n#up w\n#down s\n#left a\n#right d\n#start f\n\n; Shared hidden variables for the UI\n#action \"fa#\"\n#status \"fs#\"\n\n; Produces the name of the queue variable for the given tier and item.\n#queue_str \"cq\"\n#queue(tier, item) -1 + 10 * ({item}) + {tier}\n\n; Raw access to the queue. Not preferred, but better than using \"queue\"\n; directly. Use for low-level optimization.\n#get_raw(raw_item) global.double.get({queue_str} . (100 + {raw_item}))\n#set_raw(raw_item, value) global.double.set({queue_str} . (100 + {raw_item}), {value})\n\n; Helper macros for operating on the craft_queue. These should be\n; used instead of direct access whenever possible.\n#get_queue(tier, item) {get_raw({queue({tier},{item})})}\n#set_queue(tier, item, value) {set_raw({queue({tier},{item})},{value})}\n\n; Add/subtract a value from the queue. Although it expands to the same thing,\n; it saves a lot of boilerplate in the source and makes it easier to understand\n; what's going on.\n#add_queue(tier, item, value) {set_queue({tier},{item},{get_queue({tier},{item})} + {value})}\n#subtract_queue(tier, item, value) {set_queue({tier},{item},{get_queue({tier},{item})} - ({value}))}\n\n; Used with factory.composite_string to extract the name from the\n; returned data.\n#get_name sub(itemdata, 0, index(itemdata, \" \", 0))\n\n; These macros can only be called *after* factory.composite_string,\n; i.e. later on in the script.\n#items_count {lua(return factory.items_count)}\n#tiers_char sub(itemdata, {lua(return factory.entry_size - 1)}, 1)\n\n{lua(\\\n factory = {};\\\n factory.items = {};\\\n factory.item_names = {};\\\n factory.categories = {};\\\n factory.prods = {};\\\n factory.prod_machines = {};\\\n factory.name_max_size = 0;\\\n factory.game_name_sizes = {};\\\n factory.game_name_sizes.item = 0;\\\n factory.game_name_sizes.craft = 0;\\\n factory.game_name_sizes.group = 0;\\\n factory.game_name_sizes.special = 0;\\\n factory.group_map = {};\\\n factory.group_map.item = 0;\\\n factory.group_map.craft = 1;\\\n factory.group_map.group = 2;\\\n factory.group_map.special = 2; --[[Intentionally the same]]\\\n\\\n function factory.add_item(name, tiers, game_name, craft_type)\\\n local item = {};\\\n item.id = #factory.items + 1;\\\n if item.id > 89 then\\\n return \"Too many items: Over the limit while adding \" .. name;\\\n end\\\n item.name = name;\\\n item.game_name = game_name;\\\n item.tier = tiers;\\\n item.craft_type = craft_type;\\\n factory.items[item.id] = item;\\\n factory.item_names[name] = item;\\\n if #name > factory.name_max_size then\\\n factory.name_max_size = #name;\\\n end\\\n if #game_name > factory.game_name_sizes[craft_type] then\\\n factory.game_name_sizes[craft_type] = #game_name;\\\n end\\\n return \":const int \" .. name .. \" \" .. #factory.items;\\\n end\\\n\\\n function factory.composite_string(var, filter, use_group_info)\\\n local acc = {};\\\n local size = 0;\\\n local first = #factory.items;\\\n local last = 0;\\\n local group_map = factory.group_map;\\\n \\\n if filter == nil then\\\n size = math.max(factory.game_name_sizes[\"item\"], factory.game_name_sizes[\"craft\"]);\\\n else\\\n size = factory.game_name_sizes[filter];\\\n end\\\n size = size + 1;\\\n local fmt = \"%-\" .. size .. \"s%d\";\\\n size = size + 1;\\\n acc[1] = 'sub(\"';\\\n for i = 1, #factory.items do\\\n local item = factory.items[i];\\\n if filter == nil or item.craft_type == filter then\\\n if use_group_info then\\\n acc[#acc + 1] = string.format(fmt, item.game_name, group_map[item.craft_type]);\\\n else\\\n acc[#acc + 1] = string.format(fmt, item.game_name, item.tier - 1);\\\n end\\\n if i < first then first = i end\\\n if i > last then last = i end\\\n end\\\n end\\\n acc[#acc + 1] = '\", ' .. size .. \" * (\" .. var .. \"), \" .. size .. ')';\\\n factory.items_count = last - first + 1;\\\n factory.entry_size = size;\\\n return table.concat(acc);\\\n end\\\n\\\n function factory.add_category(name, ...)\\\n local category = {}\\\n factory.categories[#factory.categories + 1] = category\\\n category.name = name\\\n local name1, name2, name3 = \"default\", \"first\", \"last\"\\\n local arg1, arg2, arg3 = ...\\\n for i = 1, 3 do\\\n local trimmed = arg1:gsub(\"^ +\", \"\")\\\n local item = factory.item_names[trimmed]\\\n if not item then\\\n return \"Can't find \" .. trimmed\\\n end\\\n category[name1] = item.id\\\n name1, name2, name3 = name2, name3\\\n arg1, arg2, arg3 = arg2, arg3\\\n end\\\n end\\\n)}\n\n; Define a new production item with name \"name\", craftable from tiers 1-\"tiers\".\n; The string passed to produce() is \"game_name\".\n; It will take the next available id, starting from 1.\n#item(name, tiers, game_name) {lua(\\\n return factory.add_item(\"{name}\", {tiers}, {game_name}, \"item\")\\\n)}\n\n; Define a new production item with name \"name\", craftable from tiers 1-\"tiers\".\n; The string passed to craft() is \"game_name\".\n; It will take the next available id, starting from 1.\n#make(name, tiers, game_name) {lua(\\\n return factory.add_item(\"{name}\", {tiers}, {game_name}, \"craft\")\\\n)}\n\n; Define a new item group, i.e. a set of items designed to be scanned in the\n; crafter. Because items need a valid game_name to be passed to count() in\n; various places (or else there will be spurious logging), we use \"ore\".\n; (The value returned from count() will always be ignored.)\n#group(name, tiers) {lua(\\\n return factory.add_item(\"{name}\", {tiers}, \"ore\", \"group\")\\\n)}\n\n; Defines a \"special\" item. This has a real name, like an item, but acts\n; like an item-group in other ways. This is used for lumps and ore, which\n; require special-case handling.\n#special(name, tiers, game_name) {lua(\\\n return factory.add_item(\"{name}\", {tiers}, {game_name}, \"special\")\\\n)}\n\n; Defines a category grouping for the UI. \"First\" and \"last\" are the span of\n; the group (inclusive), in item names. Must be called *after* the relevant items\n; are defined.\n#category(name, default, first, last) {lua(\\\n return factory.add_category(\"{name}\", \"{default}\", \"{first}\", \"{last}\")\\\n)}\n"],["worker_storage_lib","; This library will find a slot in the pool of workers to use as \"permanent storage\".\n; If one doesn't already exist with the given tag, a new one will be allocated,\n; *but* it will avoid overwriting other worker names using the same sytem of tags\n; for storage. Thus, scripts can use this to cooperate without having to\n; explicitly assign worker slot numbers.\n;\n; The scheme is that a name will be prefixed with \"[TOKEN]\", where \"TOKEN\" is\n; some short, unique word that will identify the script in question. For instance,\n; the factory might use the word \"factory\".\n;\n; This library does not actually store anything in the worker name. Instead, it\n; sets the variable \"worker_slot\", which you must declare in your code.\n; If worker_slot < 100, then it found a valid slot with the worker_prefix.\n; If 100 <= worker_slot < 200, then there was no existing slot, but new data\n; can be initialized at (worker_slot - 100). If worker_slot == 200, then there\n; are no available slots at all, and you should show an error message or take\n; approriate action.\n;\n; A complete example looks like this (replace angle brackets with curlies):\n;\n; :local int worker_slot\n; \n; \n\n; =================================================================================\n\n#worker_prefix {lua(return worker_lib_prefix)}\n#worker_lacks_prefix sub(worker.name(worker_slot), 0, {len({worker_prefix})}) != \"{worker_prefix}\"\n\n; The first macro, which defines the first line and the loop variable that we\n; jump to. We also store the worker_prefix with Lua, so that we don't need to\n; repeat it on the 2nd macro.\n; We don't want to skip slot 0. Doing it like this saves a line.\n#worker_lib_line_1(worker_prefix_arg) {lua(\\\n worker_lib_prefix = \"{worker_prefix_arg}\"\\\n)}\\\nfind_worker_slot_loop: worker_slot = worker_slot + if({worker_lacks_prefix}, 1, 0)\n\n; The second macro, which iterates until we're done.\n; The test against worker.group() checks for non-existant workers. They would\n; otherwise show as acceptable candidates, and we need to keep iterating to 200\n; in that case.\n#worker_lib_line_2 gotoif(find_worker_slot_loop,\\\n worker_slot < 100 && {worker_lacks_prefix} ||\\\n worker_slot >= 100 && worker_slot < 200 && (worker.group(worker_slot - 100) == -1 || contains(sub(worker.name(worker_slot - 100), 0, 1), \"[\")))\n"],["recipes",":import factory constants\n\n{lua(\\\n factory.recipes = {};\\\n)}\n\n; Infrastructure for defining the recipe list.\n{lua(\\\n function factory.recipe(item)\\\n local recipe_item = factory.item_names[item];\\\n if not recipe_item then\\\n return \"Unknown item '\" .. item .. \"'\";\\\n end\\\n if recipe_item.recipes then\\\n return \"Duplicate recipe for '\" .. item .. \"'\";\\\n end\\\n factory.recipe_item = recipe_item;\\\n local offset = 10 * recipe_item.id;\\\n --[[There are two types of recipe list that we store: A \"forward\" list stored\\\n on the item itself, which records what items are needed to *make* that\\\n item, and a \"reverse\" list stored on the main factory object and indexed\\\n by queue id, which records what items can be *made from* the item.\\\n The forward list is used by the item group system, while the reverse list\\\n is used by the recipe system proper.]]\\\n for i = offset, offset + recipe_item.tier - 1 do\\\n if not factory.recipes[i] then\\\n factory.recipes[i] = {};\\\n end\\\n end\\\n recipe_item.recipes = {};\\\n for i = 1, recipe_item.tier do\\\n recipe_item.recipes[i] = {};\\\n end\\\n end\\\n\\\n function factory.component(tiers, item_name, counts)\\\n local recipe_item = factory.recipe_item;\\\n if not recipe_item then\\\n return \"Tried to define a component before calling recipe!\"\\\n end\\\n local item_name = item_name:gsub(\" \", \"\");\\\n local item = factory.item_names[item_name];\\\n if not item then\\\n return \"Unknown item '\" .. item_name .. \"'\";\\\n end\\\n if item.craft_type == \"group\" then\\\n return string.format(\\\n [[Cannot add item \"%s\" because it is a group]],\\\n item.name,\\\n recipe_item.name,\\\n recipe_item.craft_type\\\n );\\\n end\\\n local tier_tmp = {};\\\n for word in tiers:gmatch(\"%S+\") do\\\n local tier = tonumber(word);\\\n if not tier then\\\n return \"Bad tier \" .. word\\\n end\\\n tier_tmp[#tier_tmp + 1] = tier;\\\n end\\\n if #tier_tmp ~= 1 and #tier_tmp ~= recipe_item.tier then\\\n return \"Tiers list has wrong size, item \" ..\\\n recipe_item.name .. \" has \" .. recipe_item.tier .. \" tiers\";\\\n end\\\n \\\n local base_tier;\\\n for i = 1, recipe_item.tier do\\\n base_tier = tier_tmp[i] or base_tier;\\\n local tier = base_tier;\\\n if tier < 1 then\\\n tier = tier + i;\\\n end\\\n tier_tmp[i] = tier;\\\n end\\\n \\\n local counts_tmp = {};\\\n for word in counts:gmatch(\"%S+\") do\\\n local count = tonumber(word);\\\n if not count then\\\n return \"Bad count \" .. word\\\n end\\\n counts_tmp[#counts_tmp + 1] = count;\\\n end\\\n if #counts_tmp ~= 1 and #counts_tmp ~= recipe_item.tier then\\\n return \"Counts list has wrong size, item \" ..\\\n recipe_item.name .. \" has \" .. recipe_item.tier .. \" tiers\";\\\n end\\\n for i = #counts_tmp + 1, recipe_item.tier do\\\n counts_tmp[i] = counts_tmp[i-1];\\\n end\\\n --[[Finally, merge the two expanded lists and distribute the results\\\n across the recipes for each tier. These recipes are \"reversed,\"\\\n in the sense that they don't describe how to make each item, but\\\n rather all the items that this one can be used to help make.\\\n It's this reversed list that is needed for computing the material\\\n needs when crafting.]]\\\n local recipes = factory.recipes;\\\n local offset = recipe_item.id * 10 - 1;\\\n for i = 1, recipe_item.tier do\\\n local tier = tier_tmp[i];\\\n local count = counts_tmp[i];\\\n if count > 0 and tier > 0 then\\\n --[[Tiers less than 1 can happen naturally, due to the negative tier\\\n convention, so they are silently dropped. Tiers greater than the\\\n the maximum number of tiers are an error.]]\\\n if tier > item.tier then\\\n return \"Tier \" .. tier .. \" > item max tier \" .. item.tier;\\\n end\\\n local idx = item.id * 10 + tier - 1;\\\n local comp = recipes[idx];\\\n if comp == nil then\\\n comp = {};\\\n recipes[idx] = comp;\\\n end\\\n comp[#comp + 1] = offset + i;\\\n comp[#comp + 1] = count;\\\n local item_recipe = recipe_item.recipes[i];\\\n item_recipe[#item_recipe + 1] = idx;\\\n item_recipe[#item_recipe + 1] = count;\\\n end\\\n end\\\n end\\\n\\\n function factory.produce(item, source, mult, machine)\\\n local recipe_item = factory.item_names[item];\\\n if not recipe_item then\\\n return \"Unknown item '\" .. item .. \"'\";\\\n end\\\n if recipe_item.recipes then\\\n return \"Duplicate recipe for '\" .. item .. \"'\";\\\n end\\\n local source_item = factory.item_names[source];\\\n if not source_item then\\\n return \"Unknown item '\" .. source .. \"'\";\\\n end\\\n if source_item.tier ~= recipe_item.tier then\\\n return \"Tier mismatch: \" .. source .. \"(\" .. source_item.tier ..\\\n \") vs \" .. item .. \"(\" .. recipe_item.tier .. \")\"\\\n end\\\n if #factory.prods == 0 or factory.prods[#factory.prods].machine ~= machine then\\\n if factory.prod_machines[machine] then\\\n return \"Found 2nd group for machine '\" .. machine .. \"'\";\\\n end\\\n factory.prod_machines[machine] = #factory.prods + 1;\\\n factory.prod_machines[#factory.prod_machines + 1] = machine;\\\n end\\\n local prod = {};\\\n prod.item = recipe_item;\\\n prod.source = source_item;\\\n prod.mult = mult;\\\n prod.machine = machine;\\\n factory.prods[#factory.prods + 1] = prod;\\\n\\\n local offset = 10 * source_item.id;\\\n local count = 1 / mult;\\\n for i = offset, offset + recipe_item.tier - 1 do\\\n if not factory.recipes[i] then\\\n factory.recipes[i] = {};\\\n end\\\n local recipe = factory.recipes[i];\\\n recipe[#recipe + 1] = 10 * recipe_item.id - offset + i;\\\n recipe[#recipe + 1] = count;\\\n end\\\n recipe_item.recipes = {};\\\n for i = 1, recipe_item.tier do\\\n local recipe = {};\\\n recipe[1] = offset + i - 1;\\\n recipe[2] = count;\\\n recipe_item.recipes[i] = recipe;\\\n end\\\n end\\\n\\\n function factory.check_recipes()\\\n for i = 1, #factory.items do\\\n local item = factory.items[i];\\\n if not item.recipes then\\\n return item.name .. \"(\" .. item.id .. \") has no assigned recipe!\";\\\n end\\\n end\\\n end\\\n)}\n\n; Every recipe begins with this line. There must be one (and only one)\n; recipe declaration for each item, even if there is no way to make it\n; (like for rubber).\n#recipe(item) {lua(return factory.recipe([[{item}]]))}\n\n; After starting a recipe, list its components with this. A component\n; declares a single item that is used, and the the tiers and counts of that\n; item for each tier of the recipe_item that's being made.\n; The list on the left side is the tiers, the list on the right is the counts.\n;\n; For instance:\n; recipe(white_producer)\n; component(2 3 5 7 9, screw, 2 4 4 4 4)\n; Means that a T1 white_producer needs 2 T2 screws, a T2 white_producer needs\n; 4 T3 screws, T3 requires 4 T5 screws, etc.\n; \n; There are additional space-saving wrinkles. Instead of repeating a number\n; for every tier or every count when it's always the same, you can list it\n; once. This is all-or-nothing: It must be exactly once, or listed once for\n; every tier in the recipe.\n; Also, in the tier list you can use numbers less than 1. This means that\n; the position in the list will be added to the number. I.e. a value of\n; 0 becomes equal to \"current_tier\", -1 becomes \"current_tier - 1\", etc.\n#component(tiers, item, counts) {lua(\\\n return factory.component([[{tiers}]],[[{item}]],[[{counts}]]);\\\n)}\n\n; For items that are made in machines, use this macro instead. This defines\n; the needed recipe automatically. The third argument is the \"multiplier\",\n; which says how many multiples of the item are made from a single source\n; input.\n; It is assumed that the production happens for all tiers of the item,\n; from the same source tier; things like mixing or boiling dust aren't handled\n; through this system. (Boiling isn't handled at all.)\n#produce(item, source, mult, machine) {lua(\\\n return factory.produce(\\\n [[{item}]],\\\n string.gsub([[{source}]], \" \", \"\"),\\\n {mult},\\\n string.gsub([[{machine}]], \" \", \"\")\\\n );\\\n)}\n\n; ===== BEGIN RECIPES =====\n\n{recipe(town_producer)}\n{component(2 3 5 7 9, screw, 2 4 4 4 4)}\n{component(2 0 0 0 0, plate, 2 0 0 0 0)}\n{component(0 3 5 7 9, board, 0 2 2 2 6)}\n{component(-1, chip, 0 2 2 2 4)}\n{component(-1, town_producer, 1)}\n\n{recipe(factory_producer)}\n{component(1 3 5 7 9, wire, 1 4 4 4 4)}\n{component(2 0 0 0 0, screw, 1 0 0 0 0)}\n{component(0 3 5 7 9, board, 0 1 1 1 3)}\n{component(2 3 5 0 0, plate, 2 2 2 0 0)}\n{component(0 0 0 7 9, dense_plate, 0 0 0 2 4)}\n{component(1 1 2 3 4, chip, 2 1 1 1 3)}\n{component(-1, factory_producer, 1)}\n\n{recipe(mine_producer)}\n{component(2 3 5 7 9, screw, 2 2 2 4 4)}\n{component(1 2 4 6 8, wire, 2 3 2 5 5)}\n{component(2 0 5 7 9, plate, 1 0 1 2 2)}\n{component(0 3 5 7 9, dense_plate, 0 2 2 2 2)}\n{component(1 1 2 3 4, chip, 1)}\n{component(-1, mine_producer, 1)}\n\n{recipe(workshop_producer)}\n{component(0 2 4 6 8, wire, 0 4 2 8 8)}\n{component(1 3 5 7 9, wire, 4 2 2 2 2)}\n{component(2 3 5 7 9, plate, 1 2 2 2 2)}\n{component(1 0 2 3 4, chip, 1 0 2 2 2)}\n{component(-1, workshop_producer, 1)}\n\n{recipe(construction_firm_producer)}\n{component(2 4 6 8 10, rod, 3 4 10 10 10)}\n{component(2 4 6 8 10, plate, 2)}\n{component(1 1 2 3 4, chip, 1 2 2 2 2)}\n{component(-1, construction_firm_producer, 1)}\n\n{recipe(laboratory_producer)}\n{component(0 3 5 7 9, pipe, 0 3 5 10 14)}\n{component(2 4 6 8 10, motor, 1)}\n{component(1 3 5 7 9, dense_plate, 2 2 4 4 6)}\n{component(0, chip, 3 2 4 4 6)}\n{component(-1, laboratory_producer, 1)}\n\n{recipe(headquarters_producer)}\n{component(1 3 5 7 9, wire, 2 4 8 8 12)}\n{component(2 4 6 8 10, motor, 1 2 2 2 2)}\n{component(0, chip, 3 2 4 4 6)}\n{component(-1, headquarters_producer, 1)}\n\n{recipe(powerplant_producer)}\n{component(1 3 5 7 9, insul_cable, 2 2 4 4 6)}\n{component(2 4 6 8 10, motor, 1)}\n{component(0 3 5 7 9, block, 0 3 5 5 14)}\n{component(0, chip, 3 2 4 4 6)}\n{component(-1, powerplant_producer, 1)}\n\n{recipe(arcade_producer)}\n{component(2 4 6 8 9, insul_cable, 4 6 6 6 6)}\n{component(2 4 6 8 10, pipe, 4 4 4 4 8)}\n{component(0, chip, 4 4 4 4 6)}\n{component(-1, arcade_producer, 1)}\n\n{recipe(tradingpost_producer)}\n{component(2 4 6 8 10, ring, 6 8 8 8 12)}\n{component(2 4 6 8 10, plate, 4)}\n{component(0, chip, 2 2 2 2 4)}\n{component(-1, tradingpost_producer, 1)}\n\n{recipe(shipyard_producer)}\n{component(2 4 6 8 10, insul_cable, 6 8 8 8 12)}\n{component(1 3 5 7 9, block, 4)}\n{component(0, chip, 2 2 2 2 4)}\n{component(-1, shipyard_producer, 1)}\n\n{recipe(museum_producer)}\n{component(3 5 7 9 10, insul_cable, 6 7 7 7 9)}\n{component(2 4 6 8 10, block, 4 5 5 5 7)}\n{component(0, chip, 2 2 2 2 4)}\n{component(-1, museum_producer, 1)}\n\n{recipe(statue_of_cubos_producer)}\n{component(1 3 5 7 9, dense_block, 4 5 5 5 7)}\n{component(2 4 6 8 10, motor, 2)}\n{component(2 4 6 8 10, pipe, 2)}\n{component(2 4 6 8 10, pump, 2 3 3 3 5)}\n{component(0, chip, 2 2 2 2 4)}\n{component(-1, statue_of_cubos_producer, 1)}\n\n{recipe(gem_producer)}\n{component(5, chip, 10)}\n{component(4, chip, 10)}\n{component(10, insul_cable, 2)}\n{component(1, dense_block, 1)}\n{component(2, dense_block, 1)}\n{component(3, dense_block, 1)}\n{component(4, dense_block, 1)}\n{component(5, dense_block, 1)}\n{component(6, dense_block, 1)}\n{component(7, dense_block, 1)}\n{component(8, dense_block, 1)}\n{component(9, dense_block, 1)}\n{component(10, dense_block, 1)}\n\n{recipe(exotic_producer)}\n{component(5, chip, 10)}\n{component(10, insul_cable, 2)}\n{component(10, dense_block, 10)}\n{component(10, assembly, 1)}\n{component(10, boiler, 1)}\n{component(10, crusher, 1)}\n{component(10, cutter, 1)}\n{component(10, mixer, 1)}\n{component(10, oven, 1)}\n{component(10, presser, 1)}\n{component(10, refiner, 1)}\n{component(10, shaper, 1)}\n{component(10, belt, 1)}\n\n{recipe(acceleration_booster)}\n{component(2 3 5, chip, 1 4 6)}\n{component(4, plate, 4 0 0)}\n{component(1, rainbow_plate, 0 2 4)}\n{component(4 8 0, circuit, 4 8 0)}\n{component(10, dense_block, 0 0 12)}\n{component(1, void_essence, 0 0 4)}\n{component(-1, acceleration_booster, 2)}\n\n{recipe(machine_booster)}\n{component(2 3 5, chip, 2)}\n{component(4 7 10, pipe, 4)}\n{component(4, wire, 2 0 0)}\n{component(0 7 10, dense_block, 0 2 2)}\n{component(8, dense_plate, 0 6 0)}\n{component(1, rainbow_plate, 0 0 4)}\n{component(1, void_essence, 0 0 2)}\n{component(4, circuit, 1 0 0)}\n{component(-1, machine_booster, 1)}\n\n{recipe(production_booster)}\n{component(1 3 0, chip, 4 4 0)}\n{component(2 7 0, plate, 4 6 0)}\n{component(2 7 10, circuit, 1 4 4)}\n{component(10, dense_block, 0 0 12)}\n{component(1, void_essence, 0 0 4)}\n{component(-1, production_booster, 0 1 4)}\n\n{recipe(resource_booster)}\n{component(3, plate, 6 0 0)}\n{component(1, rainbow_plate, 0 0 2)}\n{component(1 3 4, chip, 2 2 6)}\n{component(4 8 0, circuit, 1 2 0)}\n{component(0 7 10, block, 0 4 4)}\n{component(1, void_essence, 0 0 1)}\n{component(-1, resource_booster, 0 1 2)}\n\n{recipe(pumpkin_producer)}\n{component(0, carved_pumpkin, 6)}\n{component(0, anti_pumpkin, 6)}\n{component(0, pumpkin_plate, 20)}\n\n{recipe(oven)}\n{component(0, plate, 4 6 8 8 8 8 8 8 8 8)}\n{component(0, insul_cable, 2 2 3 3 3 4 4 4 4 4)}\n{component(0, block, 0 0 0 0 0 2 2 2 2 2)}\n{component(-1, oven, 1)}\n\n{recipe(presser)}\n{component(1, hammer, 2 0 0 0 0 0 0 0 0 0)}\n{component(0, plate, 4 5 7 7 7 9 9 9 9 9)}\n{component(0, wire, 1 1 2 2 2 3 3 3 3 3)}\n{component(0, block, 0 0 0 0 4 5 5 5 5 5)}\n{component(1 1 1 2 2 2 3 3 4 4, chip, 2)}\n{component(-1, presser, 1)}\n\n{recipe(assembly)}\n{component(0, pipe, 1 1 1 1 1 2 2 2 2 2)}\n{component(0, dense_plate, 6 5 8 8 8 10 10 10 12 12)}\n{component(0, motor, 1 1 1 1 1 1 1 1 2 2)}\n{component(1 1 1 2 2 2 3 3 4 4, chip, 1 1 1 1 1 1 1 1 1 2)}\n{component(-1, assembly, 1)}\n\n{recipe(refiner)}\n{component(0, block, 0 0 0 0 0 0 5 5 5 7)}\n{component(0, dense_plate, 4)}\n{component(0, motor, 1 1 1 1 2 2 2 2 2 4)}\n{component(0, ring, 1 2 2 2 3 3 3 3 3 5)}\n{component(0, pump, 1 2 2 2 3 3 3 3 3 5)}\n{component(1 1 2 2 2 2 3 4 4 5, chip, 2)}\n{component(-1, refiner, 1)}\n\n{recipe(mixer)}\n{component(0, dense_plate, 5 4 4 5 5 6 6 6 6 6)}\n{component(0, motor, 2)}\n{component(0, pump, 1 1 1 2 2 3 3 3 3 3)}\n{component(1 1 1 2 2 2 2 3 4 4, chip, 1 1 1 2 2 3 3 3 3 3)}\n{component(-1, mixer, 1)}\n\n{recipe(crusher)}\n{component(0, dense_plate, 7 8 8 8 8 8 8 9 9 9)}\n{component(0, motor, 1 1 1 1 1 1 1 2 2 2)}\n{component(1 1 1 2 2 2 2 2 4 4, chip, 1 2 2 2 2 2 2 3 3 3)}\n{component(-1, crusher, 1)}\n\n{recipe(belt)}\n{component(1, rubber, 3 4 4 4 4 0 0 0 0 0)}\n{component(1, rubber_plate, 0 0 0 0 0 4 5 5 5 5)}\n{component(0, motor, 3 3 3 3 3 3 4 4 4 4)}\n{component(0, insul_cable, 3 4 4 4 4 4 5 5 5 5)}\n{component(0 0 0 1 2 2 3 3 4 4, chip, 0 0 0 4 4 4 5 5 5 5)}\n{component(-1, belt, 1)}\n\n{recipe(cutter)}\n{component(0, plate, 2 2 2 2 2 2 2 0 0 0)}\n{component(0, dense_plate, 3 3 3 3 3 4 4 2 2 2)}\n{component(0, block, 0 0 0 0 0 0 0 4 4 6)}\n{component(0, motor, 3 4 4 4 4 5 5 5 5 7)}\n{component(-1, cutter, 1)}\n\n{recipe(shaper)}\n{component(0, plate, 4 4 4 4 4 4 4 4 4 0)}\n{component(0, dense_plate, 0 0 0 0 0 0 0 0 0 4)}\n{component(0, screw, 1 2 2 2 2 3 3 3 3 3)}\n{component(0, block, 1 2 2 2 2 3 3 3 3 0)}\n{component(0, dense_block, 0 0 0 0 0 0 0 0 0 5)}\n{component(0, motor, 2)}\n{component(0, insul_cable, 1 1 1 1 1 2 2 2 2 4)}\n{component(-1, shaper, 1)}\n\n{recipe(boiler)}\n{component(0, wire, 2 2 2 3 3 3 3 3 4 5)}\n{component(0, dense_plate, 2 2 2 3 3 3 3 3 4 5)}\n{component(0, block, 4 7 7 8 8 8 8 8 9 10)}\n{component(0, motor, 1 1 1 2 2 2 2 2 3 4)}\n{component(0, screw, 2)}\n{component(0, pump, 1)}\n{component(-1, boiler, 1)}\n\n{recipe(rainbow_dust)}\n{component(1, dust, 1)}\n{component(2, dust, 1)}\n{component(3, dust, 1)}\n{component(4, dust, 1)}\n{component(5, dust, 1)}\n{component(6, dust, 1)}\n{component(7, dust, 1)}\n{component(8, dust, 1)}\n{component(9, dust, 1)}\n{component(10, dust, 1)}\n\n{recipe(chip)}\n{component(1 3 5 7 9, circuit, 2)}\n{component(2 4 6 8 10, circuit, 2 4 4 2 2)}\n{component(1 3 5 7 9, board, 1 4 4 6 8)}\n{component(2 4 6 8 10, board, 1 2 2 6 8)}\n{component(-1, chip, 0 4 8 12 12)}\n\n{recipe(insul_cable)}\n{component(0, cable, 1 1 1 2 3 4 5 10 12 16)}\n{component(1, rubber, 1 2 0 0 0 0 0 0 0 0)}\n{component(1, rubber_plate, 0 0 2 4 6 8 10 10 12 16)}\n\n{recipe(stacked_plate)}\n{component(0, plate, 9)}\n\n{recipe(stacked_pumpkin)}\n{component(0, pumpkin, 9)}\n\n{recipe(motor)}\n{component(0, plate, 4)}\n{component(0, screw, 1)}\n{component(0, rod, 2)}\n{component(0, wire, 1)}\n{component(1, rubber, 1)}\n\n{recipe(pump)}\n{component(0, plate, 2)}\n{component(0, motor, 1)}\n{component(0, ring, 2)}\n{component(1, rubber_plate, 4)}\n\n{recipe(hammer)}\n{component(2, ingot, 6)}\n{component(2, rod, 1)}\n\n{recipe(block)}\n{component(0, dense_plate, 8 8 8 8 8 8 12 12 12 12)}\n\n{recipe(rubber_sapling)}\n{component(0, rubber, 8)}\n{component(9, ore, 1)}\n\n{recipe(void_sapling)}\n{component(1, rainbow_dust, 8)}\n{component(1, rubber_sapling, 1)}\n\n{recipe(producers)}\n#recipe_producers(x) \\\n{component(0, town_producer, {x})}\\\n{component(0, factory_producer, {x})}\\\n{component(0, mine_producer, {x})}\\\n{component(0, workshop_producer, {x})}\\\n{component(0, construction_firm_producer, {x})}\\\n{component(0, laboratory_producer, {x})}\\\n{component(0, headquarters_producer, {x})}\\\n{component(0, powerplant_producer, {x})}\\\n{component(0, arcade_producer, {x})}\\\n{component(0, tradingpost_producer, {x})}\\\n{component(0, shipyard_producer, {x})}\\\n{component(0, museum_producer, {x})}\\\n{component(0, statue_of_cubos_producer, {x})}\n{recipe_producers(1)}\n\n{recipe(machines)}\n{component(0, oven, 1)}\n{component(0, presser, 1)}\n{component(0, assembly, 1)}\n{component(0, refiner, 1)}\n{component(0, mixer, 1)}\n{component(0, crusher, 1)}\n{component(0, belt, 1)}\n{component(0, cutter, 1)}\n{component(0, shaper, 1)}\n{component(0, boiler, 1)}\n\n{recipe(parts)}\n#recipe_parts \\\n{component(0, insul_cable, 1)}\\\n{component(0, stacked_plate, 1)}\\\n{component(0, motor, 1)}\\\n{component(0, pump, 1)}\\\n{component(1, hammer, 0 1 0 0 0 0 0 0 0 0)}\\\n{component(0, block, 1)}\\\n{component(0, ingot, 1)}\\\n{component(0, plate, 1)}\\\n{component(0, dense_plate, 1)}\\\n{component(0, dense_block, 1)}\\\n{component(0, pipe, 1)}\\\n{component(0, cable, 1)}\\\n{component(0, wire, 1)}\\\n{component(0, rod, 1)}\\\n{component(0, ring, 1)}\\\n{component(0, screw, 1)}\\\n{component(0, board, 1)}\\\n{component(0, circuit, 1)}\\\n{component(0, rubber_plate, 1 0 0 0 0 0 0 0 0 0)}\\\n{component(1, rubber_sapling, 0 0 0 0 0 0 0 0 1 0)}\n{recipe_parts}\n\n{recipe(chips)}\n{component(1, chip, 1)}\n{component(2, chip, 1)}\n{component(3, chip, 1)}\n{component(4, chip, 1)}\n{component(5, chip, 1)}\n\n{recipe(chip_and_prods)}\n{component(0, chip, 1)}\n{recipe_producers(1)}\n\n#recipe_machines_and_parts \\\n{component(0, oven, 1)}\\\n{component(1, presser, 0 1 0 0 0 0 0 0 0 0)}\\\n{component(0, presser, 0 1 1 1 1 1 1 1 1 1)}\\\n{component(0, assembly, 1)}\\\n{component(0, refiner, 1)}\\\n{component(0, mixer, 1)}\\\n{component(0, crusher, 1)}\\\n{component(0, belt, 1)}\\\n{component(0, cutter, 1)}\\\n{component(0, shaper, 1)}\\\n{component(0, boiler, 1)}\\\n{recipe_parts}\n\n{recipe(all)}\n{component(0, chip, 1 1 1 1 1 0 0 0 0 0)}\n{recipe_producers(1 1 1 1 1 0 0 0 0 0)}\n{recipe_machines_and_parts}\n\n; Machine-produced items\n{recipe(ingot)} ; Has special-case producing code\n{component(0, dust, 1)}\n\n{produce(rainbow_ingot, rainbow_dust, 1, oven)}\n\n{produce(plate, ingot, 1, presser)}\n{produce(rainbow_plate, rainbow_ingot, 1, presser)}\n{produce(dense_plate, stacked_plate, 1, presser)}\n{produce(rubber_plate, rubber, 1, presser)}\n{produce(pumpkin_plate, stacked_pumpkin, 1, presser)}\n\n{produce(dense_block, block, 1, boiler)}\n{produce(anti_pumpkin, pumpkin, 1, boiler)}\n\n{produce(rod, ingot, 2, shaper)}\n{produce(pipe, plate, 1, shaper)}\n{produce(ring, rod, 1, shaper)}\n\n{produce(cable, ingot, 2, refinery)}\n{produce(board, plate, 1, refinery)}\n{produce(wire, cable, 1, refinery)}\n\n{produce(screw, rod, 4, cutter)}\n{produce(carved_pumpkin, pumpkin, 1, cutter)}\n\n{produce(circuit, cable, 1, assembler)}\n\n; These recipes have special-case code to handle them, because of the\n; complexities involved with choosing between ore and lumps for making dust.\n{recipe(lump)}\n{recipe(dust)}\n\n; Terminal items, unable to be crafted.\n{recipe(ore)}\n{recipe(rubber)}\n{recipe(void_essence)}\n{recipe(pumpkin)}\n\n; ===== END RECIPES =====\n\n{lua(return factory.check_recipes())}\n"],["run_recipes",":import factory constants\n:import recipes\n\n:name {script_name(run_recipes)}\n\n; Debugging function, kept in case of future problems.\n#debug_dump_recipes {lua(\\\n acc = {};\\\n for i = 1, #factory.items do\\\n local item = factory.items[i];\\\n acc[#acc+1] = string.format(\"%02d %-18s [\", i, item.name);\\\n for j = 1, item.tier do\\\n if j ~= 1 then acc[#acc+1] = string.format(\"\\n%23s\", \"\") end\\\n acc[#acc+1] = \"(\";\\\n local recipe = factory.recipes[i * 10 + j - 1];\\\n for k = 1, #recipe do\\\n if k ~= 1 then acc[#acc+1] = \" \" end\\\n acc[#acc+1] = recipe[k];\\\n end\\\n acc[#acc+1] = \")\";\\\n end\\\n acc[#acc+1] = \"]\\n\";\\\n end\\\n return table.concat(acc);\\\n)}\n\n;{debug_dump_recipes}\n\n; Now that all the recipes are defined, we have to put them in a valid order.\n; We do this with a modified breadth-first-search, optimized around the\n; structure of our data.\n; Each item (which in this context is a type-tier pair, identified by\n; the formula type * 10 + tier) is sequentially checked against a graph\n; that is incrementally being formed. If all its recipe-items have already been\n; satisfied (or it has none), then it is also satisfied, and (as long\n; as it has recipe-items) it's put on a queue to be output. Otherwise, a count\n; is kept of how many unsatisfied recipes it has, and an entry is made in each\n; blocking recipe pointing back to this item.\n; After each item, the queue is processed. The head of the queue is popped\n; and gets the next sequential id; this is how the recipes get their order.\n; Also, any items blocked on it will have their tallies decremented by one.\n; If these go to zero, they are now satisfied and will be added to the queue\n; to be output, as well.\n{lua(\\\n local recipes_list = {};\\\n factory.recipes_list = recipes_list;\\\n local items = factory.items;\\\n local recipes = factory.recipes;\\\n local graph = {};\\\n local queue = {};\\\n for tier = 10, 1, -1 do\\\n for item_id = 1, #items do\\\n local item = items[item_id];\\\n local id = 10 * item_id + tier - 1;\\\n local recipe = recipes[id];\\\n if not recipe then\\\n goto continue;\\\n end\\\n local entry = graph[id];\\\n if not entry then\\\n entry = {};\\\n entry.blocking = {};\\\n graph[id] = entry;\\\n end\\\n local blockers = 0;\\\n if recipe then\\\n for i = 1, #recipe, 2 do\\\n local other = graph[recipe[i]];\\\n if not other then\\\n other = {};\\\n other.blocking = {};\\\n other.blockers = -1;\\\n graph[recipe[i]] = other;\\\n end\\\n if other.blockers ~= 0 then\\\n blockers = blockers + 1;\\\n other.blocking[#other.blocking + 1] = id;\\\n end\\\n end\\\n end\\\n entry.blockers = blockers;\\\n \\\n if blockers == 0 then\\\n queue[#queue + 1] = id;\\\n end\\\n \\\n local q_front = 1;\\\n while q_front <= #queue do\\\n id = queue[q_front];\\\n entry = graph[id];\\\n recipes_list[#recipes_list + 1] = id;\\\n for i = 1, #entry.blocking do\\\n local other = graph[entry.blocking[i]];\\\n other.blockers = other.blockers - 1;\\\n if other.blockers == 0 then;\\\n queue[#queue + 1] = entry.blocking[i];\\\n end;\\\n end\\\n q_front = q_front + 1\\\n end\\\n queue = {};\\\n ::continue::\\\n end\\\n end\\\n)}\n\n; Debugging function, kept in case of future problems.\n#debug_dump_recipes_list {lua(\\\n acc = {};\\\n for i = 1, #factory.recipes_list do\\\n local id = factory.recipes_list[i];\\\n acc[#acc+1] = string.format(\"%03d:\", id);\\\n local recipe = factory.recipes[id];\\\n for j = 1, #recipe do\\\n acc[#acc+1] = string.format(\" %3s\", recipe[j]);\\\n end\\\n acc[#acc+1] = \"\\n\";\\\n end\\\n return table.concat(acc);\\\n)}\n\n;{debug_dump_recipes_list}\n\n; Constructs the data table that is used to create loop_data. See below\n; for the format of this string. One difference is that in loop_data, \n; the previous item/current item index data is at the beginning and end of the\n; string. Here, that is actually only stored once, and the sub() read window is\n; expanded to overlap consequetive sections to pick up the previous item when\n; reading the next.\n#get_data(num_terms) {lua(\\\n local num_terms = {num_terms};\\\n local num_terms_2 = num_terms * 2;\\\n local recipe_limit = 0;\\\n local acc_main = {};\\\n local sub = string.sub;\\\n \\\n local multipliers = \"0123a b c d e f g h i j k l m n o p q r s t u v w x y z\";\\\n local mult_max = {};\\\n for i = 1, num_terms do\\\n mult_max[i] = 0;\\\n end\\\n acc_main[1] = [[\"___]];\\\n for i = 1, #factory.recipes_list do\\\n local id = factory.recipes_list[i];\\\n local recipe = factory.recipes[id];\\\n local item_id = id // 10;\\\n local limit = (#recipe - 1) // num_terms_2;\\\n if limit < 0 then limit = 0 end;\\\n for j = 1, (limit + 1) * num_terms_2, 2 do\\\n acc_main[#acc_main + 1] = (j < #recipe) and recipe[j] + 100 or \" \";\\\n local mod = (j // 2) % num_terms + 1;\\\n local mult = (j < #recipe) and recipe[j+1] * 4 or 0;\\\n if mult > mult_max[mod] then\\\n mult_max[mod] = mult;\\\n end\\\n acc_main[#acc_main + 1] = sub(multipliers, mult + 1, mult + 1);\\\n if mod == num_terms then\\\n acc_main[#acc_main + 1] = id + 100;\\\n recipe_limit = recipe_limit + 1;\\\n end\\\n end\\\n end\\\n \\\n factory.main_size = 4 * num_terms + 3;\\\n acc_main[#acc_main + 1] = '\"';\\\n factory.multipliers = {};\\\n factory.num_terms = num_terms;\\\n factory.recipe_limit = recipe_limit;\\\n for i = 1, num_terms do\\\n if mult_max[i] >= #multipliers then\\\n return \"Multiplier limit exceeded at \" .. i;\\\n end\\\n factory.multipliers[i] = sub(multipliers, 1, mult_max[i] + 1);\\\n end;\\\n return table.concat(acc_main);\\\n)}\n\n#lookup_item {lua(\\\n return factory.composite_string(\\\n string.format(\\\n [[s2i(sub(gsg({data_name}), %d * (i + 1), 2), -1) - 11]],\\\n factory.main_size\\\n ),\\\n nil,\\\n true --[[Get craft type info instead of tiers]]\\\n );\\\n)}\n\n; Returns the set of lookup/multiplier terms that will be added in to form\n; the base of the value. Most of the data for this is pre-comupted by\n; get_data().\n#recipe_terms {lua(\\\n acc = {};\\\n for i = 1, factory.num_terms do\\\n if i ~= 1 then\\\n acc[#acc + 1] = \" + \";\\\n end\\\n acc[#acc + 1] = string.format(\\\n [[max(0., ceil(gdg({queue_str} . sub(loop_data, %d, 3)) *\\\n i2d(index(\"%s\", sub(loop_data, %d, 1), 0)) * 0.25))]],\\\n factory.entry_size + 4 * i - 1,\\\n factory.multipliers[i],\\\n factory.entry_size + 4 * i + 2\\\n );\\\n end\\\n return table.concat(acc);\\\n)}\n\n; This macro is used to test the type of an item, in order to efficiently\n; disable the count. Item groups don't have a single real item associated,\n; but the count() still has to count something real to avoid a spurious log line.\n; Ore and lumps are also classified as groups so that they won't be counted,\n; because they're treated specially.\n#item_type sub(loop_data, {lua(return factory.entry_size - 1)}, 1)\n\n; These macros are used to test if the item is a dust. Dust gets its queue value\n; inflated by one, which has the effect of always ending up with 1 at the end.\n; (Although it doesn't prevent temporarily using all dust.)\n#recipe_item_trunc sub(loop_data, {lua(\\\n return factory.entry_size + factory.main_size;\\\n)}, 2)\n#item_trunc(item) \"{lua(return factory.item_names[\"{item}\"].id + 10)}\"\n\n#prev_item sub(loop_data, {lua(return factory.entry_size)}, 3)\n#recipe_item sub(loop_data, {lua(\\\n return factory.entry_size + factory.main_size;\\\n)}, 3)\n; The tier value is just the last digit of recipe_item\n#tier_value sub(loop_data, {lua(\\\n return factory.entry_size + factory.main_size + 2;\\\n)}, 1)\n#recipe_item_name sub(loop_data, 0, index(loop_data, \" \", 0))\n#recipe_limit {lua(return factory.recipe_limit)}\n\n:global int factory_target\n:global double factory_target_amount\n\n; We use the name of the data variable to pull double-duty as a\n; hider for all the craft-queue variables.\n; This macro gives a prettier name for the global data string.\n#data_name \"fdata\"\n\n:local int i\n:local string loop_data\n\n; Uncomment this for debugging. It needs to be before the data-hiding block.\n; Also uncomment the line below.\n;gss(\"debug\", \"\")\n\ngss({data_name}, {get_data(4)})\nloop:\n\n; Evaluates to an expression that results in an string containing encoded data\n; for this recipe. The first entry_size-1 characters are the in-game item name,\n; space-padded. Then comes the item type info: 0 is a regular item, 1 is a\n; crafted item, and 2 is an item-group. After that is a series of 3-number\n; strings, each of which is a craft-queue index.\n; The first is the index for the previous item, which is used to determine if\n; this is a continuation from a previous line. The next `num_terms`\n; terms are index values for queue values to add. After each term is a single\n; character which is a multiplier value. Following that is an index for the\n; current item, which is used as the index to set, and possibly also as an\n; index to read from.\n;\n; All of this is pulled from reading the appropriate sections of \"data\", mostly\n; as-is. However, there is a secondary lookup for the item name.\nloop_data = {lookup_item} . sub(\\\n gsg({data_name}),\\\n i * {lua(return factory.main_size)},\\\n {lua(return factory.main_size + 3)}\\\n)\n\n; The core expression that does all the work. If this item is the target item,\n; then set the queue value to factory_target_amount - this ensures that the\n; target is always made, even if it already exists.\n; Otherwise, we set it to the sum of all of its recipe terms, minus the existing\n; count. This core value is the \"queue value\", and equals how many must be\n; crafted (if positive) or how many extra we have (if negative).\n; Since we are hardcoding the number of recipe terms that are handled in each\n; loop iteration to a small constant (4), there are additional wrinkles because\n; we may need to process the same item multiple times to get all the recipe\n; terms in. This means that if we're seeing the same item again, we add the\n; previous value of the variable and skip subtracting the count.\n; We also skip the count if the item is a group, since those don't have valid\n; items to count anyway.\n; In this way, we efficiently encode a sum that requires multiple passes.\nglobal.double.set({queue_str} . {recipe_item},\\\n if({recipe_item} == i2s(factory_target + 100),\\\n factory_target_amount,\\\n (\\\n if({prev_item} == {recipe_item}, global.double.get({queue_str} . {recipe_item}), 0.) +\\\n {recipe_terms} -\\\n if(\\\n {prev_item} == {recipe_item} || {item_type} == \"2\",\\\n 0.,\\\n if({recipe_item_trunc} == {item_trunc(dust)}, -1., 0.) + count({recipe_item_name}, index(\" 0123456789\", {tier_value}, 0))\\\n )\\\n )\\\n )\\\n)\ni = i + 1\n; Uncomment this line for debugging. It will show the quantities and ids of all\n; items that need to be produced. (The ids will be +100, because of internal reasons.)\n; You will also need to enable another line above.\n;gss(\"debug\", gsg(\"debug\") . if(gdg({queue_str} . {recipe_item}) > 0., \"
\" . {recipe_item} . \" \" . gdg({queue_str} . {recipe_item}), \"\"))\ngotoif(loop, i < {recipe_limit})\n\n; inv_tier = 10 - tier. Represented this way so we can use the fact that locals\n; automatically start at 0.\n:local int inv_tier\n\ntierloop:\n\n{add_queue(10 - inv_tier, dust, max(0., ({get_queue(10 - inv_tier, lump)} - count(\"lump\", 10 - inv_tier)) * 4.0))}\n{add_queue(10 - inv_tier, ore, max(0., min(count(\"ore\", 10 - inv_tier), ceil({get_queue(10 - inv_tier, dust)} / 2.))))}\n\ngotoif(nolumps, inv_tier == 9 || count(\"ore\", 10 - inv_tier) + count(\"dust\", 10 - inv_tier) == 0.)\n{add_queue(9 - inv_tier, lump, max(0., {get_queue(10 - inv_tier, dust)} - {get_queue(10 - inv_tier, ore)} * 2.))}\nnolumps:\n\ninv_tier += 1\ngotoif(tierloop, inv_tier < 10)\n\n; If we're missing the resources needed to complete the recipe, set error.\ngss(\\\n {action},\\\n if(\\\n {get_queue(1, ore)} * 2. < {get_queue(1, dust)},\\\n \"Missing \" . ({get_queue(1, dust)} - {get_queue(1, ore)} * 2.) . \" T1 dust
and/or higher tier dust/ore!\",\\\n if(\\\n {get_queue(1, rubber)} > 0.,\\\n \"Missing \" . {get_queue(1, rubber)} . \" rubber!\",\\\n if(\\\n {get_queue(1, void_essence)} > 0.,\\\n \"Missing \" . {get_queue(1, void_essence)} . \" void essence!\",\\\n if(\\\n {get_queue(1, pumpkin)} > 0.,\\\n \"Missing \" . {get_queue(1, pumpkin)} . \" pumpkin(s)!\",\\\n \"\"\\\n )\\\n )\\\n )\\\n )\\\n)\n\n:local int k\nunset_loop:\n; If there's an error, clear the queue. We take advantage of initial value being 0 to\n; cut down on lines. The first item is (raw) id 110, corresponding to\n; 100 offset + 1 * 10, so we start at 109 as a no-op. We don't use unset here to avoid\n; reordering issues with other scripts; all the global variables we will ever need to\n; hide will have been set by this point, and we don't want to leave holes.\n\n; Note that it is important to skip setting variables that are 0, since those might\n; be tiers that are never calculated - in those cases, they would be new variables\n; outside the hiding block if we set them here (after the first run of the factory).\nglobal.double.set({queue_str} . (109 + k), 0.)\nskip_unset:\nk += 1\ngotoif(\\\n if(global.double.get({queue_str} . (109 + k)) == 0., skip_unset, unset_loop),\\\n contains(gsg({action}), \"<\") && k < {lua(return #factory.items * 10 + 1)}\\\n)\n\ngss(\"fhide2\", \"\")\n"],["produce",":import factory constants\n:import recipes\n\n:name {script_name(produce)}\n\n:local int i\n:local int machine_idx\n:local double previous_amount\n:local string machine_chunk\n:local string machine_name\n:local string produce_data\n:local string source_name_raw\n\nkey.{left}()\nkey.{right}()\n\nisopen(\"factory\")\n\n; If we were launched via key impulse, invoke the UI immediately to start turbo,\n; and set action to signal which key was pressed. Otherwise, we're being called\n; as a produce script.\nexecute(if(contains(impulse(), \"key.\"), \"{script_name(ui)}\", \"###badname###\"))\ngotoif(end, contains(impulse(), \"key.\"))\n\n; ================== BEGIN MACROS + LUA ==================\n; The data for producing is defined in a single string, stored in\n; produce_data. Each entry in the string is a \"produce_chunk\", defined\n; in the following format:\n; MULT SOURCE_CHAR DEST_ITEM\n; DEST_ITEM is a 3-byte number, denoting the raw queue value for the item.\n; (I.e. after the +100 addition.) SOURCE_CHAR is a single character that\n; is used for indexing a separate lookup table of the source item names.\n; (The source tier will be the same as the tier of DEST_ITEM.) The MULT\n; is a single number used when items produce multiple output copies.\n#produce_chunk_size 5\n\n; Here we precompute all our data tables. There are three tables:\n; produce_data, which stores the produce chunks described above.\n; item_names, which stores destination item names only, and\n; machine_data, which stores a combination of machine name and offset into produce_data.\n;\n; We also compute the character lookup table that is used to convert DEST_ITEM to/from\n; numbers for use with item_names.\n{lua(\\\n local format = string.format\\\n\\\n machine_chunk_size = nil\\\n machine_data = nil\\\n item_names = nil\\\n item_names_size = nil\\\n trans_table = \"0123456789abcdefghijklmnopqrstuvwxyz\"\\\n produce_data = nil\\\n\\\n local function pad_strings(arr)\\\n local max = 0\\\n for i = 1, #arr do\\\n max = max >= #arr[i] and max or #arr[i]\\\n end\\\n max = max + 1\\\n local fmt_str = format(\"%%%ds\", -max)\\\n local res = {}\\\n for i = 1, #arr do\\\n res[i] = format(fmt_str, arr[i])\\\n end\\\n return res, max\\\n end\\\n\\\n local machine_acc = {}\\\n local item_acc = {}\\\n local prod_acc = {}\\\n local prods_offset = 1\\\n local offsets = {}\\\n factory.prod_machines[#factory.prod_machines + 1] = \"end\"\\\n factory.prod_machines[\"end\"] = #factory.prods + 1\\\n for i = 1, #factory.prod_machines do\\\n local machine = factory.prod_machines[i]\\\n machine_acc[i] = machine\\\n local next_offset = factory.prod_machines[machine]\\\n for j = 1, 10 do\\\n for k = prods_offset, next_offset - 1 do\\\n local prod = factory.prods[k]\\\n if prod.source.tier < j then goto continue end\\\n local source_name = prod.source.game_name\\\n if not item_acc[source_name] then\\\n item_acc[#item_acc + 1] = source_name\\\n item_acc[source_name] = #item_acc\\\n end\\\n prod_acc[#prod_acc + 1] = prod.mult ..\\\n string.sub(trans_table, item_acc[source_name], item_acc[source_name]) ..\\\n prod.item.id * 10 + 100 + j - 1\\\n ::continue::\\\n end\\\n end\\\n offsets[i] = format(\"%03d\", #prod_acc * 5)\\\n prods_offset = next_offset\\\n end\\\n if #prod_acc * 5 > 999 then\\\n return \"Offset overflows three digits: \" .. table.concat(offsets, \" \")\\\n end\\\n machine_acc, machine_chunk_size = pad_strings(machine_acc)\\\n for i = 1, #machine_acc do\\\n machine_acc[i] = offsets[i] .. machine_acc[i]\\\n end\\\n\\\n machine_data = table.concat(machine_acc)\\\n machine_chunk_size = machine_chunk_size + 3\\\n item_acc, item_names_size = pad_strings(item_acc)\\\n item_names = table.concat(item_acc)\\\n trans_table = string.sub(trans_table, 1, #item_acc)\\\n produce_data = table.concat(prod_acc)\\\n\\\n--[[Debug functions, uncomment as needed]]\\\n--[[return table.concat(prod_acc, \",\")]]\\\n)}\n\n;{lua(return produce_data)}_\n;{lua(return machine_data)}\n;{lua(return item_names)}\n;{lua(return trans_table)}_\n\n; When it is loaded from produce_data, the value in produce_data is\n; processed from the raw produce_chunk to lookup the SOURCE_CHAR from the\n; secondary lookup table. This still needs extra processing to trim the\n; trailing spaces, which is why the source_name macro exists.\n#dest_offset 2\n#source_char_offset 1\n#mult_offset 0\n#get_source_tier index(\" 0123456789\", sub(produce_data, {dest_offset} + 2, 1), 0)\n#source_name sub(source_name_raw, 0, index(source_name_raw, \" \", 0))\n#mult s2d(sub(produce_data, {mult_offset}, 1), 0.)\n\n#next_offset s2i(sub(machine_chunk, {lua(return machine_chunk_size)}, 3), 0)\n\n; Raw access to the queue. This is even more direct than\n; get_raw, because we're using queue strings directly.\n#raw_dest sub(produce_data, {dest_offset}, 3)\n#get_dest global.double.get({queue_str} . {raw_dest})\n#set_dest(value) global.double.set({queue_str} . {raw_dest}, {value})\n; ================== END MACROS + LUA ==================\n\nexecutesync(\"{script_name(produce dust)}\")\nstop(\"{script_name(produce dust)}\")\n\nmachine_loop:\n; Setup machine data variables and set the produce index to the proper place.\n; We break this out into separate variables because we don't run through this\n; section as much (only once per machine, so ~8 times), and it makes the later\n; expressions more efficient. Also, we have lines to spare.\n; We get 3 extra characters after the machine chunk so that we have the next\n; offset available as well, which we use to determine when to finish the produce_loop\n; below and procede to the next machine.\nmachine_chunk = sub(\"{lua(return machine_data)}\", machine_idx, {lua(return machine_chunk_size)} + 3)\nmachine_name = sub(machine_chunk, 3, index(machine_chunk, \" \", 0) - 3)\ni = s2i(sub(machine_chunk, 0, 3), 0)\nmachine_idx += {lua(return machine_chunk_size)}\n\n; We need to setup the production data anyway, so we branch there. This is\n; one instruction less efficient than other methods, but it lets us\n; reuse a lot of code so it's worth it.\ngoto(if(\\\n contains(\"end\", machine_name),\\\n end,\\\n set_data\\\n))\n\nproduce_loop:\nsource_name_raw = sub(\\\n \"{lua(return item_names)}\",\\\n index(\\\n \"{lua(return trans_table)}\",\\\n sub(produce_data, {source_char_offset}, 1),\\\n 0\\\n ) * {lua(return item_names_size)},\\\n {lua(return item_names_size)}\\\n)\n\n; Uncomment this line for runtime production debugging.\n; You will also need to uncomment the line in run_recipes that unhides debug.\n;gss(\"debug\", gsg(\"debug\") . \"
\" . {source_name} . {get_source_tier} . \"_\" . min(ceil({get_dest} / {mult}), count({source_name}, {get_source_tier})) . machine_name)\n\n; Just try to produce. There is a wrinkle here not present when crafting:\n; we take the minimum with the source count, so that we can use items as\n; quickly as they become available. We only craft the entire quantity, to\n; avoid needlessly filling the inventory.\nproduce(\\\n {source_name},\\\n {get_source_tier},\\\n min(ceil({get_dest} / {mult}), count({source_name}, {get_source_tier})),\\\n machine_name\\\n)\n\n; We can blindly subtract the number of items in the machine, because we've\n; guaranteed via the guards on our loop that we exit as soon as the machine\n; is active. This means the item in there *must* be the correct one, if the\n; count is > 0.\n{set_dest({get_dest} - {mult} * machine.item.count(machine_name))}\n\nnext_item:\ni += {produce_chunk_size}\nset_data:\nproduce_data = sub(\"{lua(return produce_data)}\", i, {produce_chunk_size})\n\n; Go to the next machine if we're done with this set of machines, or the machine\n; has become active. Otherwise, either produce the next item, or skip it if\n; the quantity is <= 0.\ngoto(if(\\\n i >= {next_offset},\\\n machine_loop,\\\n if(\\\n active(machine_name),\\\n machine_loop,\\\n if(\\\n {get_dest} <= 0.,\\\n next_item,\\\n produce_loop\\\n )\\\n )\\\n))\n\nend:\ngss({action}, if(\\\n contains(impulse(), \"key.\"),\\\n if(\\\n contains(impulse(), \"key.{left}\"),\\\n \"1\",\\\n \"-1\"\\\n ),\\\n gsg({action})\\\n))\n"],["produce dust",":import factory constants\n\n:name {script_name(produce dust)}\n\n:local int _tier\n:local double previous_amount\n\n_tier = _tier + 1\nprevious_amount = count(\"ore\", _tier)\nproduce(\"ore\", _tier, {get_queue(_tier, ore)}, \"crusher\")\n{subtract_queue(_tier, dust, (previous_amount - count(\"ore\", _tier)) * 2.)}\n{subtract_queue(_tier, ore, previous_amount - count(\"ore\", _tier))}\n\ngotoif(notierupdust, _tier >= 10 || {get_queue(_tier, lump)} <= 0. || active(\"mixer\"))\ncraft(\"lump\", _tier, min(\\\n min(\\\n (count(\"dust\", _tier) - 1.) / 4.,\\\n count(\"dust\", _tier + 1)\\\n ),\\\n {get_queue(_tier, lump)} - count(\"lump\", _tier)\\\n))\nprevious_amount = count(\"lump\", _tier)\nproduce(\"lump\", _tier, min(previous_amount, {get_queue(_tier, lump)}), \"mixer\")\n{subtract_queue(_tier, dust, previous_amount - count(\"lump\", _tier))}\n{subtract_queue(_tier, lump, previous_amount - count(\"lump\", _tier))}\nnotierupdust:\n\ngotoif(noproduceingot, active(\"oven\") | (if(_tier == 10, 0., ({get_queue(_tier, lump)} - count(\"lump\", min(_tier, 9)))) * 4. > 2. * min(0., count(\"dust\", _tier) - ({get_queue(_tier, ingot)})) & count(\"dust\", _tier) < 2. * ({get_queue(_tier, ingot)})))\nprevious_amount = count(\"dust\", _tier)\nproduce(\"dust\", _tier, min(previous_amount - 1., {get_queue(_tier, ingot)}), \"oven\")\n{subtract_queue(_tier, ingot, previous_amount - count(\"dust\", _tier))}\nnoproduceingot:\n\ngoto(if(_tier >= 10, 99, 1))\n"],["craft",":import factory constants\n:import recipes\n\n:name {script_name(craft)}\n\n:local double previous_amount\n:local int i\n:local string itemdata\n:local string group_data\n\n:global int factory_target\n\nkey.{up}()\nkey.{down}()\n\nisopen(\"factory\")\n\n; If we were launched via key impulse, invoke the UI immediately to start turbo,\n; and set action to signal which key was pressed. Otherwise, we're being called\n; as a produce script.\nexecute(if(contains(impulse(), \"key.\"), \"{script_name(ui)}\", \"###badname###\"))\ngotoif(group_abort, contains(impulse(), \"key.\"))\n\n; Craft all the items.\ncraftitems_loop:\nitemdata = {lua(return factory.composite_string(\"i/10\", \"craft\"))}\n\ndo_craft:\n; Because items and tiers start at 1, but i is zero-based, we have to add 1\n; or (pre-multiplying, 10) in these expressions.\n; However, tier is zero-based inside our indexing representation, so we don't\n; add it there.\nprevious_amount = count({get_name}, i%10 + 1)\ncraft({get_name}, i%10 + 1, {get_raw(10 + i)})\n{set_raw(10 + i, {get_raw(10 + i)} - (count({get_name}, i%10 + 1) - previous_amount))}\ninc:\n; This skips over tiers that don't exist for the given item, by using the\n; tier data embedded in \"itemdata\".\ni = i + if(i2s(i%10) == {tiers_char}, 10 - i%10, 1)\ngoto(if(i%10 != 0,\\\n if({get_raw(10 + i)} > 0., do_craft, inc),\\\n if(i < {items_count} * 10, craftitems_loop, crafting_done)))\ncrafting_done:\n\n; Uncomment these lines for runtime item quantity debugging.\n; You will also need to uncomment the line in run_recipes that unhides debug.\n;i = 10\n;debug_loop:\n;gss(\"debug\", if(i == 10, \"\", gsg(\"debug\") . if({get_raw(i)} > 0., \"
\" . i . \" \" . {get_raw(i)}, \"\")))\n;i += 1\n;gotoif(debug_loop, i < {lua(return #factory.items)} * 10 + 10)\n\ngroup_data = \"{lua(\\\n acc = {}\\\n for i = 1, #factory.items do\\\n local item = factory.items[i];\\\n if item.craft_type ~= \"group\" then goto continue end\\\n for j = 1, item.tier do\\\n acc[#acc+1] = \"|\" .. (i * 10 + j + 99);\\\n local recipe = item.recipes[j];\\\n for k = 1, #recipe, 2 do\\\n acc[#acc+1] = recipe[k] + 100;\\\n end\\\n end\\\n ::continue::\\\n end\\\n acc[#acc+1] = \"|\";\\\n return table.concat(acc);\\\n)}\"\n\ni = index(group_data, \"|\" . (factory_target + 100), 0) + 1\ncheck_group_loop:\ni += 3\ngoto(if(\\\n i < 4, group_abort,\\\n if(\\\n sub(group_data, i, 1) == \"|\", group_complete,\\\n if(\\\n gdg({queue_str} . sub(group_data, i, 3)) > 0., group_abort,\\\n check_group_loop\\\n )\\\n )\\\n))\n\ngroup_complete:\n{set_raw(factory_target, 0.)}\ngroup_abort:\ngss({action}, if(\\\n contains(impulse(), \"key.\"),\\\n if(\\\n contains(impulse(), \"key.{up}\"),\\\n \"1\",\\\n \"-1\"\\\n ),\\\n gsg({action})\\\n))\n"],["init",":import factory constants\n\n:name {script_name(init)}\n\nwakeup()\nopen.factory()\n\nisopen(\"factory\")\n\n:global double factory_target_amount\n:global int factory_target\n\n:global int turbo.cycles\n:global int turbo.cycles.max\n\n:local double target_value\n\n; Run the UI, to display the current pending item.\nexecute(\"{script_name(ui)}\")\n\n; Initial dispatch on entering the factory to determine if we are resuming\n; a crafting operation or waiting to launch a new one. This is done outside\n; of turbo, so that we never invoke turbo if we don't need to.\ngotoif(wait_loop, {get_raw(factory_target)} == 0.)\n\n; The core factory cycle. We do each loop of factory production within\n; one call of nested turbo start/stop, which executes within one frame.\n;\n; This loop handles both calculating recipes via \"run_recipes\"\n; and crafting via \"craft\". This is done with conditional execution,\n; in order to save lines for future possible features.\nbegin_cycle:\nexecutesync(\"TE2.2:start\")\n\n; Here we up the number of cycles so that it will be enough to\n; calculate the recipes or produce items. We intentionally have a\n; frame break between the recipe calculation and the beginning of crafting\n; to prevent jarring lag on startup.\n; This formula ensures that we don't accidentally step on another script\n; that needs more cycles, and also that we get the full amount of cycles\n; even if something else started turbo before us.\nturbo.cycles.max = max(turbo.cycles.max, turbo.cycles + 4000)\n\n; We have to save this, because the value can change as a result of executing\n; later scripts.\ntarget_value = {get_raw(factory_target)}\n\n; All conditional execution in the loop is behind this condition. If it\n; is true, then we're here because of `ui`. Otherwise,\n; this is a regular crafting iteration.\n\n; There are extra checks for isopen(\"factory\") here. The main check is at the\n; bottom of the loop, but that happens before the end of the frame, so there will\n; be one more frame where we have exited the factory, but the loop still runs.\n; Normally, this wouldn't be an issue (all of the scripts will perform no actions\n; and leave the variables in the same state), but \"produce\" and \"craft\" have a\n; startup condition of isopen(\"factory\"), since they do double-duty as UI impulse\n; scripts as well.\n; So, we have to protect them, otherwise executesync() will hang when it hits\n; the false condition, leading to a stuck script.\nexecutesync(if(\\\n isopen(\"factory\"),\\\n if(target_value > 0., \"{script_name(produce)}\", \"{script_name(run_recipes)}\"),\\\n \"###badname###\"\\\n))\nstop(if(target_value > 0., \"{script_name(produce)}\", \"{script_name(run_recipes)}\"))\nexecutesync(if(\\\n target_value > 0. && isopen(\"factory\"),\\\n \"{script_name(craft)}\",\\\n \"###badname###\"\\\n))\nstop(if(target_value > 0., \"{script_name(craft)}\", \"###badname###\"))\n\n; Clear factory_target to indicate that crafting is done, if it is, in fact, done.\nfactory_target = if({get_raw(factory_target)} > 0., factory_target, 0)\n; Re-display the UI when crafting is done.\nexecute(if(factory_target == 0, \"{script_name(ui)}\", \"###badname###\"))\n\n; Clear this (unconditionally), to signal that we're not starting a new crafting\n; pass.\nfactory_target_amount = 0.\n\nexecutesync(\"TE2.2:stop\")\n\n; Here we either return to the next iteration of the production loop,\n; or stall on this instruction until we need to launch the factory.\n; Because there is always 1 extra cycle of turbo after \"TE2.2:stop,\" there\n; is enough time to execute this goto and have only a single frame break\n; before starting turbo again at the top of the loop.\nwait_loop:\ngotoif(\\\n if(max(factory_target_amount, {get_raw(factory_target)}) > 0.,\\\n begin_cycle, wait_loop\\\n ),\\\n isopen(\"factory\")\\\n)\n\n; Remove the UI status so it doesn't clutter the variables when we're outside the\n; factory.\n; This is safe to repeat, when turbo is looping at the end of the script.\ngss({status}, \"\")\n"],["ui",":import factory constants\n:import worker_storage_lib\n\n:name {script_name(ui)}\n\n:local double count\n:local int visible_tier\n:local int tier\n:local int category\n:local int item\n:local int cursor\n\n:global int turbo.cycles\n:global int turbo.register\n:global int factory_target\n:global double factory_target_amount\n\nkey.{start}()\n\nisopen(\"factory\")\n\n; Because of how turbo exec works, we can't launch turbo on the frame the script\n; starts by calling \"TE2.2:start\", if we are being called from another script.\n; We can only do it by changing turbo.register directly, saving a layer of\n; script execution.\n; (This is because of the relative positioning of TE.turbo vs our script;\n; usually we would be before, but when we are first launched our script is after.)\nturbo.register += 1\n\n; Use worker_storage_lib to find a worker_slot to use for permanent storage.\n:local int worker_slot\n{worker_lib_line_1([factory])}\n{worker_lib_line_2}\n\n; Split out the worker data into separate variables. We take advantage of this\n; time to also perform increment/decrement, since we can easily combine it in\n; this stage. We don't bother checking if we've got a valid slot, because if we\n; don't, we'll simply fail to parse and get the fallback value.\n\n#action_num s2i(gsg({action}), 0)\n; Is the u/d action valid for this position?\n; Using contains() instead of == and chained compares saves a lot of import space.\n#is_ud(pos) contains(impulse() . cursor, \":craft{pos}\")\n\ncursor = min(9, max(0,\\\n s2i(sub(worker.name(worker_slot), {len({worker_prefix})} + 11, 1), 0) +\\\n if(contains(impulse(), \":produce\"), {action_num}, 0)\\\n))\ncount = min(9999999., max(1.,\\\n s2d(sub(worker.name(worker_slot), {len({worker_prefix})}, 7), 1.) +\\\n if(contains(impulse(), \":craft\"), i2d({action_num} * 10^(cursor - 3)), 0.)\\\n))\n\n#num_categories {lua(return #factory.categories)}\ncategory = (\\\n s2i(sub(worker.name(worker_slot), {len({worker_prefix})} + 8, 1), 0) -\\\n if({is_ud(1)}, {action_num}, 0) +\\\n {num_categories}\\\n) % {num_categories}\n\n; Because math expressions are so expensive, in terms of import space, it's best\n; to just pre-compute tables of the next/previous item to go to for every item.\n; To avoid duplicating expressions, we also make a no-op table for the case where\n; we're not incrementing or decrementing.\n{lua(\\\n function factory.item_inc_table(inc)\\\n local acc = {}\\\n local default = string.format(\"%02d\", factory.categories[1].default - 1)\\\n for i = 1, #factory.categories do\\\n local cat = factory.categories[i]\\\n local cat_size = cat.last - cat.first + 1\\\n for j = cat.first, cat.last do\\\n acc[j] = string.format(\"%02d\", (j + inc - cat.first) % cat_size + cat.first - 1)\\\n end\\\n end\\\n for i = 1, #factory.items do\\\n acc[i] = acc[i] or default\\\n end\\\n return table.concat(acc)\\\n end\\\n)}\n#item_inc_table(inc) {lua(return factory.item_inc_table({inc}))}\n#category_defaults {lua(\\\n local acc = {}\\\n for i = 1, #factory.categories do\\\n acc[i] = string.format(\"%02d\", factory.categories[i].default - 1)\\\n end\\\n return table.concat(acc)\\\n)}\n\n; Items in this variable are zero-indexed, as opposed to their regular ids, which\n; start from 1.\n; We increment the items in the opposite direction from the action, because pressing\n; up (action 1) *increments* numerical values but *decrements* elements in an\n; alphabetically sorted list.\nitem = s2i(\\\n if({is_ud(1)},\\\n sub(\\\n \"{category_defaults}\",\\\n category * 2,\\\n 2\\\n ),\\\n sub(\\\n if({is_ud(0)},\\\n if(\\\n contains(gsg({action}), \"-1\"),\\\n \"{item_inc_table(1)}\",\\\n \"{item_inc_table(-1)}\"\\\n ),\\\n \"{item_inc_table(0)}\"\\\n ),\\\n s2i(\\\n sub(worker.name(worker_slot), {len({worker_prefix})} + 9, 2),\\\n {lua(return factory.categories[1].default - 1)}\\\n ) * 2,\\\n 2\\\n )\\\n ),\\\n 0\\\n)\n\n; The distinction between \"tier\" and \"visible_tier\" has to do with the fact that\n; items have different tier maximums. When a user is scrolling through different\n; items, if the \"tier\" is T10 (represented as \"9\" in our zero-indexed scheme),\n; then the \"visible_tier\" will change to fit the maximum tier for the item, while\n; the tier remains at T10. However, if the item is a producer (max of T5), and\n; the user moves the cursor to the tier field and presses \"W\", the attempt to\n; increment \"tier\" will fix it at a new value of T5 (4).\n#tier_data {lua(\\\n local acc = {};\\\n for i = 1, #factory.items do\\\n local item = factory.items[i];\\\n acc[i] = string.format(\"%d\", item.tier - 1);\\\n end\\\n return table.concat(acc)\\\n)}\n\n; Expression to load the saved tier data from the worker name, including\n; a default value when there is no data.\n#saved_worker_tier s2i(sub(worker.name(worker_slot), {len({worker_prefix})} + 7, 1), 0)\n\n; It makes the most sense to set visible_tier first. There is a complication\n; when adjusting the tier down; in this case, we reduce the maximum bounds by\n; one, so that (for instance) if the max tier is T5, and the current tier is T10,\n; we'll properly clip the visible_tier to T4 after subtracting 1 to get T9.\n; This type of adjustment isn't needed (or wanted) in the other direction, or\n; when the tier isn't changing.\nvisible_tier = max(0,\\\n min(s2i(sub(\"{tier_data}\", item, 1), 9) +\\\n if({is_ud(2)} && gsg({action}) == \"-1\", -1, 0),\\\n {saved_worker_tier} + if({is_ud(2)}, {action_num}, 0)\\\n )\\\n)\ntier = if({is_ud(2)}, visible_tier, {saved_worker_tier})\n\n; Finally, construct the visible name for use in the variable.\n{lua(\\\n local names = {}\\\n local name_starts = {}\\\n local name_sizes = {}\\\n local name_len = 0\\\n\\\n for i = 1, #factory.items do\\\n local name = factory.items[i].name\\\n names[i] = name\\\n name_starts[i] = string.format(\"%03d\", name_len)\\\n name_len = name_len + #name\\\n name_sizes[i] = string.format(\"%02d\", #name)\\\n end\\\n factory.name_data = table.concat(names)\\\n factory.name_starts = table.concat(name_starts)\\\n factory.name_sizes = table.concat(name_sizes)\\\n factory.items_count = #factory.items\\\n\\\n names = {}\\\n for i = 1, #factory.categories do\\\n names[i] = factory.categories[i].name\\\n end\\\n factory.category_names = table.concat(names)\\\n)}\n#name_data {lua(return factory.name_data)}\n#name_starts {lua(return factory.name_starts)}\n#name_sizes {lua(return factory.name_sizes)}\n#category_names {lua(return factory.category_names)}\n\n; Set this variable in order to begin a variable-hiding block.\n; It looks like a no-op, but really we're ensuring that it has a slot in the\n; globals table, even if its value empty - in this case, what we care about\n; is the *name*, which starts with \"\".\n; Do this *after* all the other work, to give turbo register time to\n; set its variables, in case we're doing this right at startup.\ngss({action}, gsg({action}))\n\n; Do conditional stuff, depending on if we're launching the factory.\n; We set all these variables always, so that they'll have a consistent order,\n; and also to save lines instead of jumping over this block.\n\n#valid_start contains(factory_target . impulse(), \"0key.\")\n; This (maybe) launches the factory. We need to do it soon enough to not\n; interrupt turbo, see the comment on \"TE2.2:stop\", below.\n{lua(\\\n for i = 1, #factory.categories do\\\n if factory.categories[i].name == \"grup\" then\\\n grup_cat = i - 1;\\\n return;\\\n end\\\n end\\\n return \"Couldn't find grup\"\\\n)}\nfactory_target_amount = if(\\\n {valid_start},\\\n count,\\\n factory_target_amount\\\n)\n\n; Only set factory_target if it's zero, which indicates that the factory is idle.\n; This prevents repeated keypresses from messing things up.\nfactory_target = if({valid_start}, (item + 1) * 10 + visible_tier, factory_target)\n\n; Construct the status line. There's a *lot* that goes into this.\n;\n; For starters, we fake the variable - it's not really \"make\", but rather a\n; totally different variable that's less likely to collide. The true variable name\n; is never seen, because it's still part of the block. We put our fake\n; variable name after. We do all this so that we can \"unset\" the variable by\n; just changing its value - this way, it's not truly unset, and we don't have\n; issues with variable ordering as a result.\n;\n; This also means we can change the variable name to something else, like\n; \"error\", if we need to, all without actually creating a new variable or\n; changing ordering.\n;\n; All the clauses are dynamically created on the fly here. We also create the\n; \"cursor\" by highlighting a specific part of the result in green. This involves\n; a great deal more complexity.\n#curs_col 2f4\n; For later - scanning rework\n;count_string if(category == {lua(return grup_cat)}, \" -SCAN-\", d2s(10000000. + count))\n#count_string d2s(10000000. + count)\n\ngss({status}, if(\\\n worker_slot == 200,\\\n \"error=No available workers!\",\\\n if(\\\n turbo.cycles == 0,\\\n \"error=Turbo exec is not working\",\\\n \"make=\" .\\\n sub({count_string}, 1, 9 - cursor) .\\\n \"\".\\\n sub({count_string}, 10 - cursor, 1) .\\\n \"\".\\\n sub({count_string}, 11 - cursor, 10) .\\\n \"xT\", \"fff>T\") .\\\n (visible_tier + 1) .\\\n if(cursor == 1, \" \", \" \") .\\\n sub(\"{category_names}\", category * 4, 4) .\\\n if(cursor == 0, \" ...
\", \" ...
\") .\\\n sub(\"{name_data}\", s2i(sub(\"{name_starts}\", item * 3, 3), 0),\\\n s2i(sub(\"{name_sizes}\", item * 2, 2), 0)) .\\\n \"
\" . if(\\\n contains(impulse() . gsg({action}), \":init<\"),\\\n gsg({action}),\\\n if(\\\n factory_target == 0,\\\n \"{up}{left}{down}{right} moves, {start} crafts \",\\\n \"Crafting...\"\\\n )\\\n )\\\n )\\\n))\n\n; Pause/unpause a dissolve worker, if it occupies our data slot.\n; If it's a different type of worker, leave it alone by dummying out the name.\nworker.pauseName(\\\n if(\\\n contains(worker.task(worker.name(worker_slot)), \"factory.dissolveManagement\"),\\\n worker.name(worker_slot),\\\n \"![@#nosuchtask#@]!\"\\\n ),\\\n contains(gsg({status}), \"Crafting\")\\\n)\n\n; \"init\" will take over as soon as factory_target_amount gets set 4 lines\n; above; it will call its own \"TE2.2:start\" and thus prevent the frame from\n; ending before this line takes effect.\n; We need two cycles in-between to have a seamless transition.\n; We change the variable directly, instead of calling \"TE2.2:stop\", for parity\n; with the way we start: This avoids some edge cases when turbo is incorrectly\n; installed. It also is fine in this case, because we don't need to wait for\n; the end of the frame.\nturbo.register -= 1\n\n; Setting the data back in the worker is moved way down to the bottom, to function\n; as the \"filler\" action that can be repeated while the script is waiting for\n; the frame to end. We can't use of the actions that set global variables for this,\n; because they may need to be modified later in the frame, and thus setting them\n; here in a loop would overwrite the value.\nworker.setName(if(worker_slot < 100, worker_slot, worker_slot - 100),\\\n \"{worker_prefix}\" . sub(d2s(10000000. + count), 1, 7) . tier . category .\\\n sub(i2s(100 + item), 1, 2) . cursor\\\n)\n"]]}}
```
The workspace should have the following scripts, in this order:
@@ -30,6 +30,17 @@ The workspace should have the following scripts, in this order:
## Changelog
+### V3.2.3
+
+Fix a long-standing minor bug where switching items/counts after failing to
+build due to not-enough resources would sometimes produce "extra" items when
+successfully crafting later, if you didn't reset the AI after the failure.
+This was due to leftovers in the crafting queue.
+
+```
+Bundle size: 22092 (70165 uncompressed) Scripts: 6 Max lines: 16
+```
+
### V3.2.2
Add machine speed boosters.
diff --git a/factory/factory_constants.tpt2 b/factory/factory_constants.tpt2
index 2022a59..4cfff0c 100644
--- a/factory/factory_constants.tpt2
+++ b/factory/factory_constants.tpt2
@@ -1,6 +1,6 @@
:import factory macros
-#version v3.2.2
+#version v3.2.3
; Everything in the list below is a valid target for automation, and can be
; assigned to the "target_type" variable in "lanuch factory craft".
diff --git a/factory/run_recipes.tpt2 b/factory/run_recipes.tpt2
index f84f947..31d32a4 100644
--- a/factory/run_recipes.tpt2
+++ b/factory/run_recipes.tpt2
@@ -305,22 +305,21 @@ i = i + 1
;gss("debug", gsg("debug") . if(gdg({queue_str} . {recipe_item}) > 0., "
" . {recipe_item} . " " . gdg({queue_str} . {recipe_item}), ""))
gotoif(loop, i < {recipe_limit})
-; This used to be a global variable, but we don't need that anymore now that
-; production is algorithmic.
-:local int _tier
+; inv_tier = 10 - tier. Represented this way so we can use the fact that locals
+; automatically start at 0.
+:local int inv_tier
-_tier = 10
tierloop:
-{add_queue(_tier, dust, max(0., ({get_queue(_tier, lump)} - if(_tier == 10, 0., count("lump", min(9, _tier)))) * 4.0))}
-{add_queue(_tier, ore, max(0., min(count("ore", _tier), ceil({get_queue(_tier, dust)} / 2.))))}
+{add_queue(10 - inv_tier, dust, max(0., ({get_queue(10 - inv_tier, lump)} - count("lump", 10 - inv_tier)) * 4.0))}
+{add_queue(10 - inv_tier, ore, max(0., min(count("ore", 10 - inv_tier), ceil({get_queue(10 - inv_tier, dust)} / 2.))))}
-gotoif(nolumps, _tier == 1 | count("ore", _tier) + count("dust", _tier) == 0.)
-{add_queue(-1 + _tier, lump, max(0., {get_queue(_tier, dust)} - {get_queue(_tier, ore)} * 2.))}
+gotoif(nolumps, inv_tier == 9 || count("ore", 10 - inv_tier) + count("dust", 10 - inv_tier) == 0.)
+{add_queue(9 - inv_tier, lump, max(0., {get_queue(10 - inv_tier, dust)} - {get_queue(10 - inv_tier, ore)} * 2.))}
nolumps:
-_tier -= 1
-gotoif(tierloop, _tier > 0)
+inv_tier += 1
+gotoif(tierloop, inv_tier < 10)
; If we're missing the resources needed to complete the recipe, set error.
gss(\
@@ -344,14 +343,23 @@ gss(\
)\
)
-; This must come before the unset, to avoid ordering mishaps.
-gss("fhide2", "")
+:local int k
+unset_loop:
+; If there's an error, clear the queue. We take advantage of initial value being 0 to
+; cut down on lines. The first item is (raw) id 110, corresponding to
+; 100 offset + 1 * 10, so we start at 109 as a no-op. We don't use unset here to avoid
+; reordering issues with other scripts; all the global variables we will ever need to
+; hide will have been set by this point, and we don't want to leave holes.
+
+; Note that it is important to skip setting variables that are 0, since those might
+; be tiers that are never calculated - in those cases, they would be new variables
+; outside the hiding block if we set them here (after the first run of the factory).
+global.double.set({queue_str} . (109 + k), 0.)
+skip_unset:
+k += 1
+gotoif(\
+ if(global.double.get({queue_str} . (109 + k)) == 0., skip_unset, unset_loop),\
+ contains(gsg({action}), "<") && k < {lua(return #factory.items * 10 + 1)}\
+)
-; If there's an error, unset target to cancel crafting and clean up future
-; iterations. (This leaves some mess behind if the target is changed to something
-; else without resetting AI, but it should be good enough.)
-{set_raw(factory_target, if(\
- contains(gsg({action}), "<"),\
- 0.,\
- {get_raw(factory_target)}\
-))}
+gss("fhide2", "")