z_mirror/Krastorio2

git clone https://git.raiguard.me/z_mirror/Krastorio2

Caleb HeuerFix warnings in data-utilf534e8d3

discrete-steel-pipes
16.8 KiB480 linesraw
1local flib_position = require("__flib__.position")
2local flib_table = require("__flib__.table")
3
4local data_util = {}
5
6--- Overwrites or adds ammo effects for the given technology.
7--- @param tech data.TechnologyPrototype
8--- @param effects data.Modifier[]
9function data_util.update_ammo_effects(tech, effects)
10  if not tech.effects then
11    tech.effects = {}
12  end
13
14  for _, updated_effect in pairs(effects) do
15    local exists = false
16    for _, effect in pairs(tech.effects) do
17      if
18        effect.type == updated_effect.type
19        and (
20          (effect.ammo_category ~= nil and effect.ammo_category == updated_effect.ammo_category)
21          or (effect.turret_id ~= nil and effect.turret_id == updated_effect.turret_id)
22        )
23      then
24        effect.modifier = updated_effect.modifier --- @diagnostic disable-line:assign-type-mismatch
25        exists = true
26        break
27      end
28    end
29    if not exists then
30      table.insert(
31        tech.effects,
32        { type = updated_effect.type, ammo_category = updated_effect.ammo_category, modifier = updated_effect.modifier } --- @diagnostic disable-line:assign-type-mismatch
33      )
34    end
35  end
36end
37
38--- @param technology_name data.TechnologyID
39--- @param new_effect data.Modifier
40function data_util.add_effect(technology_name, new_effect)
41  local technology = data.raw.technology[technology_name]
42  if not technology then
43    error("Technology " .. technology_name .. " does not exist.")
44  end
45  local effects = technology.effects
46  if not effects or not next(effects) then
47    technology.effects = { new_effect }
48    return
49  end
50
51  local found = false
52  for _, effect in pairs(effects) do
53    if effect.type == new_effect.type then
54      -- TODO: Update for new effect types.
55      if effect.type == "gun-speed" then
56        if effect.ammo_category == new_effect.ammo_category and effect.modifier == new_effect.modifier then
57          found = true
58          break
59        end
60      elseif effect.type == "ammo-damage" then
61        if effect.ammo_category == new_effect.ammo_category and effect.modifier == new_effect.modifier then
62          found = true
63          break
64        end
65      elseif effect.type == "give-item" then
66        if effect.item == new_effect.item and effect.count == new_effect.count then
67          found = true
68          break
69        end
70      elseif effect.type == "turret-attack" then
71        if effect.turret_id == new_effect.turret_id and effect.modifier == new_effect.modifier then
72          found = true
73          break
74        end
75      elseif effect.type == "unlock-recipe" then
76        if effect.recipe == new_effect.recipe then
77          found = true
78          break
79        end
80      elseif effect.type == "nothing" then
81        if effect.effect_description == new_effect.effect_description then
82          found = true
83          break
84        end
85      else
86        if effect.modifier == new_effect.modifier then
87          found = true
88          break
89        end
90      end
91    end
92  end
93
94  if not found then
95    table.insert(effects, new_effect)
96  end
97end
98
99--- Adds the given recipe as an unlock of the given technology.
100--- @param technology_name data.TechnologyID
101--- @param recipe_name data.RecipeID
102function data_util.add_recipe_unlock(technology_name, recipe_name)
103  local technology = data.raw.technology[technology_name]
104  if not technology then
105    error("Technology " .. technology_name .. " does not exist.")
106  end
107  for _, effect in pairs(technology.effects) do
108    if effect.type == "unlock-recipe" and effect.recipe == recipe_name then
109      error("Technology " .. technology_name .. " already unlocks recipe " .. recipe_name .. ".")
110    end
111  end
112  table.insert(technology.effects, { type = "unlock-recipe", recipe = recipe_name })
113end
114
115--- Removes the given recipe from the unlocks of the given technology.
116--- @param technology_name data.TechnologyID
117--- @param recipe_name data.RecipeID
118function data_util.remove_recipe_unlock(technology_name, recipe_name)
119  local technology = data.raw.technology[technology_name]
120  if not technology then
121    error("Technology " .. technology_name .. " does not exist.")
122  end
123  for i, effect in pairs(technology.effects) do
124    if effect.type == "unlock-recipe" and effect.recipe == recipe_name then
125      table.remove(technology.effects, i)
126      return
127    end
128  end
129  error("Technology " .. technology_name .. " does not unlock recipe " .. recipe_name .. ".")
130end
131
132--- Adds the given prerequisite to the technology.
133--- @param technology_id data.TechnologyID
134--- @param prerequisite_id data.TechnologyID
135function data_util.add_prerequisite(technology_id, prerequisite_id)
136  local technology = data.raw.technology[technology_id]
137  if not technology then
138    error("Technology " .. technology_id .. " does not exist.")
139  end
140  local prerequisite = data.raw.technology[prerequisite_id]
141  if not prerequisite then
142    error("Technology prerequisite " .. prerequisite_id .. " does not exist.")
143  end
144  if not technology.prerequisites then
145    technology.prerequisites = { prerequisite_id }
146    return
147  end
148  if flib_table.find(technology.prerequisites, prerequisite_id) then
149    error("Technology " .. technology_id .. " already has prerequisite " .. prerequisite_id)
150  end
151  table.insert(technology.prerequisites, prerequisite_id)
152end
153
154--- Removes the given prerequisite from the technology.
155--- @param technology_id data.TechnologyID
156--- @param prerequisite_id data.TechnologyID
157function data_util.remove_prerequisite(technology_id, prerequisite_id)
158  local technology = data.raw.technology[technology_id]
159  if not technology then
160    error("Technology " .. technology_id .. " does not exist.")
161  end
162  local prerequisite = data.raw.technology[prerequisite_id]
163  if not prerequisite then
164    error("Technology prerequisite " .. prerequisite_id .. " does not exist.")
165  end
166  local i = flib_table.find(technology.prerequisites or {}, prerequisite_id)
167  if not i then
168    error("Technology " .. technology_id .. " does not have prerequisite " .. prerequisite_id)
169  end
170  table.remove(technology.prerequisites, i)
171end
172
173--- Converts one research unit ingredient to another, updating science pack prerequisites if needed.
174--- @param technology_id data.TechnologyID
175--- @param from_id data.ItemID
176--- @param to_id data.ItemID
177function data_util.convert_research_unit_ingredient(technology_id, from_id, to_id)
178  local technology = data.raw.technology[technology_id]
179  if not technology then
180    error("Technology " .. technology_id .. " does not exist.")
181  end
182  if not technology.unit or not technology.unit.ingredients then
183    error("Technology " .. technology_id .. " has no research unit ingredients.")
184  end
185  local converted = false
186  for _, ingredient in pairs(technology.unit.ingredients) do
187    if ingredient[1] == from_id then
188      converted = true
189      ingredient[1] = to_id
190    elseif ingredient[1] == to_id then
191      error("Technology " .. technology_id .. " already has research unit ingredient " .. to_id)
192    end
193  end
194  if not converted then
195    error("Technology " .. technology_id .. " does not have research unit ingredient " .. from_id)
196  end
197  -- XXX: Hardcoding the science pack name like this feels bad, but it's what the old code did...
198  local from_index = flib_table.find(technology.prerequisites, from_id)
199  if from_index then
200    table.remove(technology.prerequisites, from_index)
201  end
202  local new_tech = data.raw.technology[to_id]
203  if not new_tech then
204    log("Aborting due to lack of " .. to_id .. " tech")
205    return
206  end
207  if not technology.prerequisites then
208    technology.prerequisites = { to_id }
209    return
210  end
211  if not flib_table.find(technology.prerequisites, to_id) then
212    log("Adding prerequisite " .. to_id)
213    table.insert(technology.prerequisites, to_id)
214  end
215end
216
217--- Adds the given research ingredient to the technology.
218--- @param technology_id data.TechnologyID
219--- @param ingredient_id data.ItemID
220--- @param ingredient_count uint? Defaults to 1.
221function data_util.add_research_unit_ingredient(technology_id, ingredient_id, ingredient_count)
222  ingredient_count = ingredient_count or 1
223  local technology = data.raw.technology[technology_id]
224  if not technology then
225    error("Technology " .. technology_id .. " does not exist.")
226  end
227  if technology.research_trigger then
228    error("Technology " .. technology_id .. " has a research trigger, so cannot have ingredients.")
229  end
230  local unit = technology.unit
231  if not unit then
232    error("Technology " .. technology_id .. " has no research unit.")
233  end
234  for _, ingredient in pairs(technology.unit.ingredients) do
235    if ingredient[1] == ingredient_id then
236      error("Technology " .. technology_id .. " already has research unit ingredient" .. ingredient_id)
237    end
238  end
239  table.insert(technology.unit.ingredients, { ingredient_id, ingredient_count })
240end
241
242--- Removes the given research ingredient from the technology.
243--- @param technology_id data.TechnologyID
244--- @param ingredient_id data.ItemID
245function data_util.remove_research_unit_ingredient(technology_id, ingredient_id)
246  local technology = data.raw.technology[technology_id]
247  if not technology then
248    error("Technology " .. technology_id .. " does not exist.")
249  end
250  if not technology.unit then
251    error("Technology " .. technology_id .. " has no research units.")
252  end
253  for i, ingredient in pairs(technology.unit.ingredients) do
254    if ingredient[1] == ingredient_id then
255      table.remove(technology.unit.ingredients, i)
256      return
257    end
258  end
259  error("Technology " .. technology_id .. " does not have research unit ingredient " .. ingredient_id .. ".")
260end
261
262--- Returns a technology that unlocks the given recipe. The technology returned will be the one that was added earliest.
263--- @param recipe_id data.RecipeID
264--- @return data.TechnologyPrototype?
265function data_util.get_technology_for_recipe(recipe_id)
266  for _, technology in pairs(data.raw.technology) do
267    for _, effect in pairs(technology.effects or {}) do
268      if effect.type == "unlock-recipe" and effect.recipe == recipe_id then
269        return technology
270      end
271    end
272  end
273end
274
275--- Converts a furnace prototype into an assembling machine prototype.
276--- @param furnace_name data.EntityID
277--- @return data.AssemblingMachinePrototype
278function data_util.furnace_to_assembler(furnace_name)
279  local furnace = data.raw.furnace[furnace_name]
280  if not furnace then
281    error("Furnace " .. furnace_name .. " does not exist.")
282  end
283
284  local assembler = table.deepcopy(furnace) --[[@as data.AssemblingMachinePrototype]]
285  assembler.type = "assembling-machine"
286  assembler.source_inventory_size = nil --- @diagnostic disable-line
287  assembler.energy_source.emissions_per_minute = { pollution = 2 }
288  assembler.energy_usage = "0.2MW"
289  data.raw.furnace[furnace_name] = nil
290  data:extend({ assembler })
291  return assembler
292end
293
294--- Returns a copy of the given prototype's icons in the standard format.
295--- @param prototype k2.PrototypeWithIcons
296--- @return data.IconData[]
297function data_util.get_icons(prototype)
298  local icons = prototype.icons -- LuaLS isn't inferring the dropped optional unless we assign it to a local first.
299  if icons then
300    return table.deepcopy(icons)
301  end
302  return { { icon = prototype.icon, icon_size = prototype.icon_size } }
303end
304
305--- Converts the given prototype's icons to the standard format and returns a reference to them.
306--- The game will throw an error if you provide an empty icons array, so if using the function, care must be taken to
307--- ensure that at least one icon layer exists at tne end of loading.
308--- @param prototype k2.PrototypeWithIcons
309--- @return data.IconData[]
310function data_util.standardize_icons(prototype)
311  if not prototype.icons then
312    prototype.icons = {}
313    if prototype.icon then
314      table.insert(prototype.icons, { icon = prototype.icon, icon_size = prototype.icon_size })
315      prototype.icon = nil
316      prototype.icon_size = nil
317    end
318  end
319  return prototype.icons
320end
321
322--- Overrides the prototype's icon.
323--- @param prototype k2.PrototypeWithIcons
324--- @param icon data.FileName
325--- @param icon_size double?
326function data_util.set_icon(prototype, icon, icon_size)
327  assert(prototype, "Provided prototype was nil.")
328  if prototype.icons then
329    prototype.icons = nil
330  end
331  prototype.icon = icon
332  prototype.icon_size = icon_size
333end
334
335--- Overrides the prototype's icons array.
336--- @param prototype k2.PrototypeWithIcons
337--- @param icons data.IconData[]
338function data_util.set_icons(prototype, icons)
339  assert(prototype, "Provided prototype was nil.")
340  prototype.icons = icons
341  prototype.icon = nil
342  prototype.icon_size = nil
343end
344
345--- Fields are analagous to `data.IconData`.
346--- @class k2.IconTransformOptions
347--- @field shift Vector?
348--- @field scale double?
349
350--- Applies the given transformations to each layer in the icons array in-place and returns the array.
351--- @param icons data.IconData[]
352--- @param transforms k2.IconTransformOptions
353--- @return data.IconData[]
354function data_util.transform_icons(icons, transforms)
355  for _, icon in pairs(icons) do
356    if transforms.shift then
357      icon.shift = flib_position.add((icon.shift or { 0, 0 }), transforms.shift)
358    end
359    if transforms.scale then
360      icon.scale = (icon.scale or 1) * transforms.scale
361    end
362  end
363  return icons
364end
365
366--- Adds or replaces the recipe's ingredient matching the given name.
367--- @param recipe_name data.RecipeID
368--- @param old_ingredient_name data.FluidID|data.ItemID
369--- @param new_ingredient data.IngredientPrototype
370function data_util.add_or_replace_ingredient(recipe_name, old_ingredient_name, new_ingredient)
371  local recipe = data.raw.recipe[recipe_name]
372  if not recipe then
373    error("Recipe " .. recipe_name .. " does not exist.")
374  end
375  if not recipe.ingredients then
376    recipe.ingredients = { new_ingredient }
377    return
378  end
379  for i, ingredient in pairs(recipe.ingredients) do
380    if ingredient.name == old_ingredient_name then
381      recipe.ingredients[i] = new_ingredient
382      return
383    end
384  end
385  table.insert(recipe.ingredients, new_ingredient)
386end
387
388--- Converts the given ingredient to the new ingredient, carrying over other properties.
389--- @param recipe_name data.RecipeID
390--- @param old_ingredient_name data.FluidID|data.ItemID
391--- @param new_ingredient_name data.FluidID|data.ItemID
392function data_util.convert_ingredient(recipe_name, old_ingredient_name, new_ingredient_name)
393  local recipe = data.raw.recipe[recipe_name]
394  if not recipe then
395    error("Recipe " .. recipe_name .. " does not exist.")
396  end
397  if not recipe.ingredients then
398    error("Recipe " .. recipe_name .. " has no ingredients.")
399    return
400  end
401  for _, ingredient in pairs(recipe.ingredients) do
402    if ingredient.name == old_ingredient_name then
403      ingredient.name = new_ingredient_name
404      break
405    end
406  end
407end
408
409--- Remove the given ingredient from the recipe.
410--- @param recipe_name data.RecipeID
411--- @param ingredient_name data.FluidID|data.ItemID
412function data_util.remove_ingredient(recipe_name, ingredient_name)
413  local recipe = data.raw.recipe[recipe_name]
414  if not recipe then
415    error("Recipe " .. recipe_name .. " does not exist.")
416  end
417  if not recipe.ingredients then
418    error("Recipe " .. recipe_name .. " has no ingredients.")
419    return
420  end
421  for i, ingredient in pairs(recipe.ingredients) do
422    if ingredient.name == ingredient_name then
423      table.remove(recipe.ingredients, i)
424      return
425    end
426  end
427  error("Recipe " .. recipe_name .. " does not have ingredient " .. ingredient_name .. ".")
428end
429
430--- Adds or replaces the recipe's product matching the given name.
431--- @param recipe_name data.RecipeID
432--- @param old_product_name data.FluidID|data.ItemID
433--- @param new_product data.ProductPrototype
434function data_util.add_or_replace_product(recipe_name, old_product_name, new_product)
435  local recipe = data.raw.recipe[recipe_name]
436  if not recipe then
437    error("Recipe " .. recipe_name .. " does not exist.")
438  end
439  if not recipe.results then
440    recipe.results = { new_product }
441    return
442  end
443  for i, product in pairs(recipe.results) do
444    if product.name == old_product_name then
445      recipe.results[i] = new_product
446      return
447    end
448  end
449  table.insert(recipe.results, new_product)
450end
451
452--- @param lab_name data.EntityID
453--- @param input_name data.ItemID
454function data_util.remove_lab_input(lab_name, input_name)
455  local lab = data.raw.lab[lab_name]
456  assert(lab, "Lab " .. lab_name .. " does not exist.")
457  local inputs = lab.inputs
458  assert(inputs, "Lab inputs are nil.")
459  for i = 1, #inputs do
460    if inputs[i] == input_name then
461      table.remove(inputs, i)
462      return
463    end
464  end
465  error("Lab " .. lab_name .. " does not contain input " .. input_name .. ".")
466end
467
468--- @param energy_source data.EnergySource
469--- @param category_name data.FuelCategoryID
470function data_util.add_fuel_category(energy_source, category_name)
471  if energy_source.type ~= "burner" then
472    error("Energy source must be a burner to add a fuel category.")
473  end
474  if not energy_source.fuel_categories then
475    energy_source.fuel_categories = {}
476  end
477  table.insert(energy_source.fuel_categories, category_name)
478end
479
480return data_util