factorio-process-data.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. import fs from "fs";
  2. import { checkIcons, dataRawPath, getJsonData, scriptOutputPath } from "./helpers/file.helper.ts";
  3. import type {
  4. DataRawDump,
  5. Item,
  6. Machine,
  7. Module,
  8. Quality,
  9. RecipeCategory,
  10. Signal,
  11. } from "./factorio-process-data.models.js";
  12. import { groupPrototypes, type Group } from "./helpers/groups.helper.ts";
  13. import { parseRecipe, type Recipe } from "./helpers/recipes.helper.ts";
  14. import type { ProcessedData } from "./factorio-process-data.models.ts";
  15. import { buildIconTextureMap, walkForIcons } from "./helpers/icons.helper.ts";
  16. import path from "path";
  17. //const modsPath = `${factorioPath}/mods`;
  18. // const tempPath = "./scripts/temp";
  19. // const tempIconsPath = `${tempPath}/icons`;
  20. const isHidden = (o: unknown) => !(o as { hidden?: boolean }).hidden;
  21. /**
  22. * Extract machines.
  23. */
  24. function extractMachines(data: DataRawDump): Machine[] {
  25. return Object.values(data["assembling-machine"])
  26. .filter(isHidden)
  27. .map((machine) => ({
  28. name: machine.name,
  29. icon: `entity/${machine.name}.png`,
  30. crafting_categories: machine.crafting_categories,
  31. crafting_speed: machine.crafting_speed,
  32. allowed_effects: machine.allowed_effects,
  33. module_slots: machine.module_slots,
  34. selection_box: machine.selection_box,
  35. effect_receiver: machine.effect_receiver,
  36. }));
  37. }
  38. /**
  39. * Extract modules.
  40. */
  41. function extractModules(data: DataRawDump): Module[] {
  42. return Object.values(data.module)
  43. .filter(isHidden)
  44. .map((module) => ({
  45. name: module.name,
  46. icon: `item/${module.name}.png`,
  47. subgroup: module.subgroup,
  48. category: module.category,
  49. tier: module.tier,
  50. order: module.order,
  51. effect: module.effect,
  52. }));
  53. }
  54. // TODO : Add quality Modules and quality Machines
  55. /**
  56. * All the keys that contain item‑like entities.
  57. */
  58. const ITEM_KEYS = [
  59. "item",
  60. "ammo",
  61. "armor",
  62. "capsule",
  63. "gun",
  64. "item-with-entity-data",
  65. "module",
  66. "rail-planner",
  67. "repair-tool",
  68. "selection-tool",
  69. "spidertron-remote",
  70. "space-platform-starter-pack",
  71. "tool",
  72. "fluid",
  73. ] as const;
  74. /**
  75. * Extract all items (and item‑like things) from the raw dump.
  76. */
  77. function extractItems(dataRaw: DataRawDump): Record<string, Item> {
  78. return ITEM_KEYS.reduce((acc: Record<string, Item>, key) => {
  79. const data = dataRaw[key];
  80. if (data)
  81. Object.values(data)
  82. .filter(isHidden)
  83. .forEach((item) => {
  84. const isFluid = item.type === "fluid";
  85. acc[item.name] = {
  86. name: item.name,
  87. icon: `${isFluid ? "fluid" : "item"}/${item.name}.png`,
  88. subgroup: item.subgroup ?? (isFluid ? "fluid" : "other"),
  89. order: item.order,
  90. };
  91. });
  92. return acc;
  93. }, {});
  94. }
  95. export async function processData(outputFolder: string) {
  96. const data = getJsonData(dataRawPath) as DataRawDump;
  97. const items = extractItems(data);
  98. const machines = extractMachines(data);
  99. const modules = extractModules(data);
  100. const recipeCategories: RecipeCategory[] = Object.values(data["recipe-category"])
  101. .filter(isHidden)
  102. .map((c) => ({ name: c.name, subgroup: c.subgroup, order: c.order }));
  103. const recipes = Object.values(data["recipe"])
  104. .filter(isHidden)
  105. .map((o) => parseRecipe(o, items));
  106. const qualityLevels: Quality[] = Object.values(data.quality)
  107. .filter(isHidden)
  108. .map((o) => ({
  109. ...o,
  110. icon: `quality/${o.name}.png`,
  111. }));
  112. const recipeGroup: Array<Group<Recipe>> = groupPrototypes(recipes, data, false);
  113. const signals: Array<Signal> = [];
  114. for (let item of Object.values(items)) {
  115. signals.push({
  116. name: item.name,
  117. subgroup: item.subgroup ?? "",
  118. icon: item.icon,
  119. order: item.order,
  120. });
  121. }
  122. const virtualSignals = Object.values(data["virtual-signal"]).filter(isHidden);
  123. for (let vs of virtualSignals) {
  124. signals.push({
  125. name: vs.name,
  126. subgroup: vs.subgroup ?? "",
  127. icon: `virtual-signal/${vs.name}.png`,
  128. order: vs.order,
  129. });
  130. }
  131. for (let quality of qualityLevels) {
  132. signals.push({
  133. name: quality.name,
  134. subgroup: quality.subgroup ?? "",
  135. icon: quality.icon,
  136. order: quality.order,
  137. });
  138. }
  139. const signalGroup: Array<Group<Signal>> = groupPrototypes(signals, data, false);
  140. const output: ProcessedData = {
  141. qualityLevels,
  142. machines,
  143. modules,
  144. recipeCategories,
  145. recipeGroup,
  146. signalGroup,
  147. };
  148. fs.mkdirSync(outputFolder, { recursive: true });
  149. fs.writeFileSync(
  150. path.resolve(outputFolder, "data.json"),
  151. JSON.stringify(output, null, 2),
  152. "utf-8"
  153. );
  154. const icons = new Set<string>();
  155. walkForIcons(output, icons);
  156. const iconsList = Array.from(icons).sort();
  157. checkIcons(iconsList);
  158. await buildIconTextureMap(iconsList, scriptOutputPath, outputFolder);
  159. console.log(`✅ Done - data written to ${outputFolder}`);
  160. }