more-descriptions/data-final-fixes.lua

changeset 16
2cdb59ae5fcf
parent 7
023ee666a3d6
child 17
df60e9144d82
--- a/more-descriptions/data-final-fixes.lua	Sat Jul 05 00:35:55 2025 +0300
+++ b/more-descriptions/data-final-fixes.lua	Wed Jul 23 14:46:54 2025 +0300
@@ -1,21 +1,65 @@
+---@type data.FeatureFlags
+feature_flags = feature_flags
+
 local function seconds(x)
 	return {"time-symbol-seconds", tostring(x)}
 end
 
-local function build_new_description(main_description, new_descriptions)
-	local new_localised_description = {
-		"",
-		main_description,
-	}
-	for i, k in pairs(new_descriptions)
+local energy_zero_table = {
+	["0W"] = 1,
+	["0kW"] = 1,
+	["0MW"] = 1,
+	["0GW"] = 1,
+	["0TW"] = 1,
+}
+
+---@type table<string, 1>
+local recipes_that_have_productivity_research = {}
+
+for _, technology in pairs(data.raw["technology"] or {})
+do
+	for _, effect in pairs(technology.effects or {})
 	do
-		if i ~= 1
+		if effect.type == "change-recipe-productivity"
 		then
-			table.insert(new_localised_description, "\n")
+			recipes_that_have_productivity_research[effect.recipe] = 1
 		end
-		table.insert(new_localised_description, k)
 	end
-	return new_localised_description
+end
+
+---@param x string
+local function energy_zero(x)
+	return x == nil or energy_zero_table[x]
+end
+
+---@param x LocalisedString
+-- localised string containing too many entries? snap it into many pieces and catenate them
+local function snap(x)
+	if type(x) == "table" and #x > 20 and x[1] == ""
+	then
+		local max_per_part = math.ceil((#x - 1) / 18)
+		local result = {""}
+		local segment = {""}
+		for k, v in pairs(x)
+		do
+			if k > 1
+			then
+				if #segment >= max_per_part + 1
+				then
+					table.insert(result, snap(segment))
+					segment = {""}
+				end
+				table.insert(segment, v)
+			end
+		end
+		if #segment > 1
+		then
+			table.insert(result, snap(segment))
+		end
+		return result
+	else
+		return x
+	end
 end
 
 local item_categories =
@@ -173,6 +217,26 @@
 	"solar-panel",
 }
 
+-- Find all entity categories that contain prototypes that can freeze.
+-- If a prototype in such a category cannot freeze, that's worth pointing out.
+-- However, it's not worth pointing out that the steel chest cannot freeze,
+-- since no container freezes (in vanilla anyway).
+local freezable_entity_categories = {}
+if feature_flags.freezing
+then
+	for _, category in pairs(entity_categories)
+	do
+		for _, entity in pairs(data.raw[category] or {})
+		do
+			if not energy_zero(entity.heating_energy)
+			then
+				freezable_entity_categories[entity.type] = 1
+				break
+			end
+		end
+	end
+end
+
 local function find_entity(name)
 	for _, entity_category in pairs(entity_categories)
 	do
@@ -184,6 +248,7 @@
 	return nil
 end
 
+-- @type table<string, string>
 local ammo_categories = {}
 
 for _, ammo in pairs(data.raw.ammo)
@@ -199,29 +264,43 @@
 	then
 		ammo_categories[ammo.ammo_category] = ammo_categories[ammo.ammo_category] .. "..."
 	end
+end	
+
+---@param proto data.Prototype
+---@param tooltip_field LocalisedString
+local function generic_add_description(proto, tooltip_field)
+	if not proto.custom_tooltip_fields
+	then
+		proto.custom_tooltip_fields = {}
+	end
+	table.insert(proto.custom_tooltip_fields, tooltip_field)
 end
 
 for _, item_type in pairs(item_categories)
 do
 	for _, item in pairs(data.raw[item_type] or {})
 	do
-        local new_descriptions = {}
 		local recycling_recipe = data.raw.recipe[item.name.."-recycling"]
 
-		local add_description = function(x)
-			table.insert(new_descriptions, x)
+		---@param tooltip_field data.CustomTooltipField
+		local function add_description(tooltip_field)
+			generic_add_description(item, tooltip_field)
 		end
 
 		local add_ammo_from_attack_parameters = function(attack_parameters)
 			if attack_parameters.ammo_category and ammo_categories[attack_parameters.ammo_category]
 			then
-				add_description{"more-descriptions-mod.gun-accepts-ammo", ammo_categories[attack_parameters.ammo_category]}
+				add_description{
+					name = {"more-descriptions-mod.gun-accepts-ammo"},
+					value = ammo_categories[attack_parameters.ammo_category]}
 			end
 			for _, category in pairs(attack_parameters.ammo_categories or {})
 			do
 				if ammo_categories[category]
 				then
-					add_description{"more-descriptions-mod.gun-accepts-ammo", ammo_categories[category]}
+					add_description{
+						name = {"more-descriptions-mod.gun-accepts-ammo"},
+						value = ammo_categories[category]}
 				end
 			end
 		end
@@ -234,9 +313,12 @@
 				table.insert(recycling_results, "[img="..result.type.."."..result.name.."]")
 			end
             add_description{
-                "more-descriptions-mod.recycling",
-                seconds(recycling_recipe.energy_required),
-				recycling_results
+                name = {"more-descriptions-mod.recycling"},
+                value = {
+					"more-descriptions-mod.recycling-results",
+					seconds(recycling_recipe.energy_required),
+					snap(recycling_results),
+				},
             }
 		end
 
@@ -246,31 +328,39 @@
 			if refining_recipe ~= nil
 			then
 				add_description{
-					"more-descriptions-mod.refining-cost",
-					tostring(refining_recipe.energy_required),
+					name = {"more-descriptions-mod.refining-cost"},
+					value = seconds(refining_recipe.energy_required),
 				}
 			elseif recycling_recipe ~= nil
 			then
-				add_description{"more-descriptions-mod.cannot-be-refined"}
+				add_description{
+					name = {"more-descriptions-mod.cannot-be-refined"},
+					value = ""}
 			end
 		end
 
 		if item.type == "ammo" and (item.reload_time or 0) > 0
 		then
 			add_description{
-				"more-descriptions-mod.reload-time",
-				seconds(tostring(item.reload_time / 60.0)),
+				name = {"more-descriptions-mod.reload-time"},
+				value = seconds(tostring(item.reload_time / 60.0)),
 			}
 		end
 
 		if item.type == "active-defense-equipment" and item.automatic
 		then
-			add_description{"more-descriptions-mod.fires-automatically"}
+			add_description{
+				name = {"more-descriptions-mod.fires-automatically"},
+				value = "",
+			}
 		end
 
 		if item.type == "armor" and item.provides_flight
 		then
-			add_description{"more-descriptions-mod.armor-provides-flight"}
+			add_description{
+				name = {"more-descriptions-mod.armor-provides-flight"},
+				value = "",
+			}
 		end
 
 		if item.type == "gun"
@@ -287,36 +377,66 @@
 				local width = math.ceil(cb[2][1] - cb[1][1])
 				local height = math.ceil(cb[2][2] - cb[1][2])
 				add_description{
-					"more-descriptions-mod.size",
-					tostring(width),
-					tostring(height),
+					name = {"more-descriptions-mod.entity-size"},
+					value = {
+						"more-descriptions-mod.size",
+						tostring(width),
+						tostring(height),
+					},
 				}
 			end
 
 			if entity.drops_full_belt_stacks
 			then
-				add_description{"more-descriptions-mod.drops-full-belt-stacks"}
+				add_description{
+					name = {"more-descriptions-mod.drops-full-belt-stacks"},
+					value = ""}
 			end
 
 			if entity.heat_buffer and entity.heat_buffer.specific_heat
 			then
 				add_description{
-					"more-descriptions-mod.specific-heat",
-					entity.heat_buffer.specific_heat,
+					name = {"more-descriptions-mod.specific-heat"},
+					value = {
+						"more-descriptions-mod.energy-per-degrees-celsius",
+						entity.heat_buffer.specific_heat,
+					},
 				}
 			elseif entity.energy_source
 				and entity.energy_source.type == "heat"
 				and entity.energy_source.specific_heat
 			then
 				add_description{
-					"more-descriptions-mod.specific-heat",
-					entity.energy_source.specific_heat,
+					name = {"more-descriptions-mod.specific-heat"},
+					value = entity.energy_source.specific_heat,
 				}
 			end
 
 			if entity.is_military_target
 			then
-				add_description{"more-descriptions-mod.is-military-target"}
+				add_description{
+					name = {"more-descriptions-mod.is-military-target"},
+					value = "",
+				}
+			end
+
+			if not energy_zero(entity.heating_energy or "0W")
+			then
+				local value = entity.heating_energy
+				if entity.type == "underground-belt" or entity.type == "pipe-to-ground"
+				then
+					value = {"more-descriptions-mod.value-per-end", value}
+				end
+				add_description{
+					name = {"more-descriptions-mod.heating-energy"},
+					value = value,
+				}
+			elseif freezable_entity_categories[entity.type]
+			then
+				add_description{
+					name = {"more-descriptions-mod.no-heating-energy"},
+					value = "",
+				}
 			end
 
 			if entity.type == "agricultural-tower"
@@ -328,30 +448,45 @@
 				local z = (2 * w * math.ceil((W - w) / 2 / w)) + w
 				-- num of growth cells extending from the edges of the tower
 				local r = math.floor(entity.radius) -- why is it double..?
-				add_description{"more-descriptions-mod.agricultural-tower-num-inputs",
-					tostring(entity.input_inventory_size)
+				add_description{
+					name = {"more-descriptions-mod.agricultural-tower-num-inputs"},
+					value = tostring(entity.input_inventory_size)
 				}
-				add_description{"more-descriptions-mod.agricultural-tower-growth-cell-size",
-					tostring(w)
+				add_description{
+					name = {"more-descriptions-mod.agricultural-tower-growth-cell-size"},
+					value = {
+						"more-descriptions-mod.size",
+						tostring(w),
+						tostring(w),
+					}
 				}
-				add_description{"more-descriptions-mod.agricultural-tower-growth-cell-count",
-					tostring(4 * r * (r + (z / w)))
+				add_description{
+					name = {"more-descriptions-mod.agricultural-tower-growth-cell-count"},
+					value = tostring(4 * r * (r + (z / w)))
 				}
-				add_description{"more-descriptions-mod.agricultural-tower-total-size",
-					tostring(z + 2 * r * w)
+				total_size = tostring(z + 2 * r * w)
+				add_description{
+					name = {"more-descriptions-mod.agricultural-tower-total-size"},
+					value = {
+						"more-descriptions-mod.size",
+						total_size,
+						total_size,
+					},
 				}
 			elseif entity.type == "ammo-turret"
 			then
 				if entity.energy_per_shot ~= nil
 				then
-					add_description{"more-descriptions-mod.energy-per-shot-fired",
-						entity.energy_per_shot}
+					add_description{
+						name = {"more-descriptions-mod.energy-per-shot-fired"},
+						value = entity.energy_per_shot}
 				end
 				add_ammo_from_attack_parameters(entity.attack_parameters)
 			elseif entity.type == "beacon"
 			then
-				add_description{"more-descriptions-mod.beacon-supply-area-distance",
-					tostring(entity.supply_area_distance)
+				add_description{
+					name = {"more-descriptions-mod.beacon-supply-area-distance"},
+					value = tostring(entity.supply_area_distance)
 				}
 			elseif entity.type == "car"
 			then
@@ -370,109 +505,159 @@
 				end
 				if immunities ~= ""
 				then
-					add_description{"more-descriptions-mod.car-immune-to-impacts", immunities}
+					add_description{
+						name = {"more-descriptions-mod.car-immune-to-impacts"},value = immunities}
 				end
 			elseif entity.type == "constant-combinator"
 			then
 				-- used by pushbutton mod
 				if (entity.pulse_duration or 0) > 60
 				then
-					add_description{"more-descriptions-mod.constant-combinator-pulse-duration",
-						seconds(entity.pulse_duration / 60.0)}
+					add_description{
+						name = {"more-descriptions-mod.constant-combinator-pulse-duration"},
+						value = seconds(entity.pulse_duration / 60.0),
+					}
 				elseif (entity.pulse_duration or 0) > 0
 				then
-					add_description{"more-descriptions-mod.constant-combinator-pulse-duration",
-						{"more-descriptions-mod.ticks", tostring(entity.pulse_duration)}}
+					add_description{
+						name = {"more-descriptions-mod.constant-combinator-pulse-duration"},
+						value = {
+							"more-descriptions-mod.ticks",
+							tostring(entity.pulse_duration),
+						},
+					}
 				end
 			elseif (entity.type == "container" or entity.type == "logistic-container")
 			then
 				if entity.inventory_type == "with_filters_and_bar"
 				then
-					add_description{"more-descriptions-mod.container-filters"}
+					add_description{
+						name = {"more-descriptions-mod.container-filters"},
+						value = "",
+					}
 				end
 			elseif entity.type == "cargo-wagon"
 			then
 				-- all cargo wagons support filters
-				add_description{"more-descriptions-mod.container-filters"}
+				add_description{
+					name = {"more-descriptions-mod.container-filters"},
+					value = "",
+				}
 			elseif entity.type == "display-panel"
 			then
-				add_description{"more-descriptions-mod.display-panel-max-text-width",
-					tostring(entity.max_text_width or 400)}
+				add_description{
+					name = {"more-descriptions-mod.display-panel-max-text-width"},
+					value = tostring(entity.max_text_width or 400),
+				}
 			elseif entity.type == "logistic-robot" or entity.type == "construction-robot"
 			then
 				if entity.speed_multiplier_when_out_of_energy > 0
 				then
-					add_description{"more-descriptions-mod.robot-speed-multiplier-when-out-of-energy",
-						tostring(entity.speed_multiplier_when_out_of_energy * 100)}
+					add_description{
+						name = {"more-descriptions-mod.robot-speed-multiplier-when-out-of-energy"},
+						value = tostring(entity.speed_multiplier_when_out_of_energy * 100),
+					}
 				else
-					add_description{"more-descriptions-mod.robot-crashes-when-out-of-energy"}
+					add_description{
+						name = {"more-descriptions-mod.robot-crashes-when-out-of-energy"},
+						value = "",
+					}
 				end
 			elseif entity.type == "inserter"
 			then
+				if entity.bulk
+				then
+					add_description{
+						name = {"more-descriptions-mod.inserter-bulk"},
+						value = tostring(entity.filter_count),
+					}
+				end
 				if entity.wait_for_full_hand
 				then
-					add_description{"more-descriptions-mod.inserter-wait-for-full-hand",
-					tostring(entity.filter_count)}
+					add_description{
+						name = {"more-descriptions-mod.inserter-wait-for-full-hand"},
+						value = "",
+					}
+				end
+				if entity.enter_drop_mode_if_held_stack_spoiled
+				then
+					add_description{
+						name = {"more-descriptions-mod.inserter-enters-drop-mode-if-held-stack-spoils"},
+						value = "",
+					}
 				end
 			elseif entity.type == "land-mine"
 			then
-				add_description{"more-descriptions-mod.land-mine-timeout",
-				seconds((entity.timeout or 120) / 60.0)}
+				add_description{
+					name = {"more-descriptions-mod.land-mine-timeout"},
+					value = seconds((entity.timeout or 120) / 60.0),
+				}
 			elseif entity.type == "radar"
 			then
 				if entity.connects_to_other_radars ~= false
 				then
-					add_description{"more-descriptions-mod.radar-connection"}
+					add_description{
+						name = {"more-descriptions-mod.radar-connection"},
+						value = "",
+					}
 				end
 			end
 			if entity.filter_count
 			then
-				add_description{"more-descriptions-mod.filter-count",
-					tostring(entity.filter_count)}
+				add_description{
+					name = {"more-descriptions-mod.filter-count"},
+					value = tostring(entity.filter_count),
+				}
 			end
 			for _, flag in pairs(entity.flags or {})
 			do
 				if flag == "no-automated-item-insertion"
 				then
-					table.insert(new_descriptions, {"more-descriptions-mod.no-automated-item-insertion"})
+					add_description{
+						name = {"more-descriptions-mod.no-automated-item-insertion"},
+						value = "",
+					}
 				end
 			end
 		end
-
-		if #new_descriptions > 0
-		then
-			local main_description = item.localised_description
-                and {"", item.localised_description, "\n"}
-                or {
-                    "?",
-                    {"", {"entity-description."..item.name}, "\n"},
-                    {"", {"item-description."..item.name}, "\n"},
-                    ""
-            }
-			item.localised_description = build_new_description(main_description, new_descriptions)
-		end
 	end
 end
 
 for _, recipe in pairs(data.raw.recipe)
 do
-	local new_descriptions = {}
-
+	---@param tooltip_field data.CustomTooltipField
+	local function add_description(tooltip_field)
+		generic_add_description(recipe, tooltip_field)
+	end
 	if recipe.allow_productivity
 	then
-		table.insert(new_descriptions, {"more-descriptions-mod.allows-productivity"})
+		add_description{
+			name = {"more-descriptions-mod.allows-productivity"},
+			value = "",
+		}
 	end
 
-	if #new_descriptions > 0
+	if recipes_that_have_productivity_research[recipe.name]
+	then
+		add_description{
+			name = {"more-descriptions-mod.recipe-has-productivity-research"},
+			value = "",
+		}
+	end
+
+	if recipe.result_is_always_fresh
 	then
-		local main_description = recipe.localised_description
-			and {"", recipe.localised_description, "\n"}
-			or {
-				"?",
-				{"", {"recipe-description."..recipe.name}, "\n"},
-				""
+		add_description{
+			name = {"more-descriptions-mod.recipe-result-is-always-fresh"},
+			value = "",
 		}
-		recipe.localised_description = build_new_description(main_description, new_descriptions)
+	end
+	if recipe.reset_freshness_on_craft
+	then
+		add_description{
+			name = {"more-descriptions-mod.recipe-result-freshness-reset"},
+			value = "",
+		}
 	end
 end
 
@@ -481,19 +666,27 @@
 do
 	for _, entity in pairs(data.raw[entity_category] or {})
 	do
-		local new_descriptions = {}
+		---@param tooltip_field data.CustomTooltipField
+		local function add_description(tooltip_field)
+			generic_add_description(entity, tooltip_field)
+		end
 
 		if entity.minable
 		then
-			table.insert(new_descriptions, {"more-descriptions-mod.mining-time",
-				seconds(entity.minable.mining_time)})
+			add_description{
+				name = {"more-descriptions-mod.mining-time"},
+				value = seconds(entity.minable.mining_time),
+			}
 		end
 
 		for _, flag in pairs(entity.flags or {})
 		do
 			if flag == "breaths-air"
 			then
-				table.insert(new_descriptions, {"more-descriptions-mod.breathes-air"})
+				add_description{
+					name = {"more-descriptions-mod.breathes-air"},
+					value = "",
+				}
 			end
 		end
 
@@ -501,48 +694,41 @@
 		then
 			if (entity.time_to_capture or 0) > 0
 			then
-				table.insert(new_descriptions, {"more-descriptions-mod.unit-spawner-time-to-capture",
-					seconds(entity.time_to_capture / 60.0)})
+				add_description{
+					name = {"more-descriptions-mod.unit-spawner-time-to-capture"},
+					value = seconds(entity.time_to_capture / 60.0),
+				}
 			end
 		end
-
-		if #new_descriptions > 0
-		then
-			local main_description = entity.localised_description
-				and {"", entity.localised_description, "\n"}
-				or {
-					"?",
-					{"", {"entity-description."..entity.name}, "\n"},
-					""
-			}
-			entity.localised_description = build_new_description(main_description, new_descriptions)
-		end
 	end
 end
 
-for _, space_location in pairs(data.raw["space-location"])
-do
-	local new_descriptions = {}
-
-	if (space_location.fly_condition or false)
-	then
-		table.insert(new_descriptions, {"more-descriptions-mod.space-location-fly-condition"})
-	end
-
-	if space_location.auto_save_on_first_trip == false -- (nil=true)
-	then
-		table.insert(new_descriptions, {"more-descriptions-mod.space-location-no-autosave"})
-	end
-
-	if #new_descriptions > 0
-	then
-		local main_description = space_location.localised_description
-			and {"", space_location.localised_description, "\n"}
-			or {
-				"?",
-				{"", {"space-location-description."..space_location.name}, "\n"},
-				""
-		}
-		space_location.localised_description = build_new_description(main_description, new_descriptions)
+if feature_flags.space_travel
+then
+	for _, category in pairs{"space-location", "planet"}
+	do
+		for _, space_location in pairs(data.raw[category])
+		do
+			---@param tooltip_field data.CustomTooltipField
+			local function add_description(tooltip_field)
+				generic_add_description(space_location, tooltip_field)
+			end
+			---@cast space_location data.SpaceLocationPrototype
+			local new_descriptions = {}
+			if (space_location.fly_condition or false)
+			then
+				add_description{
+					name = {"more-descriptions-mod.space-location-fly-condition"},
+					value = "",
+				}
+			end
+			if space_location.auto_save_on_first_trip == false -- (nil=true)
+			then
+				add_description{
+					name = {"more-descriptions-mod.space-location-no-autosave"},
+					value = "",
+				}
+			end
+		end
 	end
 end
\ No newline at end of file

mercurial