Add fusion lab (currently v1.2.3) fusion-lab_1.2.3

Wed, 02 Jul 2025 14:28:57 +0300

author
Teemu Piippo <teemu.s.piippo@gmail.com>
date
Wed, 02 Jul 2025 14:28:57 +0300
changeset 10
101603241531
parent 9
b0f85ff1a503
child 11
da16c5a08162

Add fusion lab (currently v1.2.3)

fusion-lab/changelog.txt file | annotate | diff | comparison | revisions
fusion-lab/control.lua file | annotate | diff | comparison | revisions
fusion-lab/data-final-fixes.lua file | annotate | diff | comparison | revisions
fusion-lab/data-updates.lua file | annotate | diff | comparison | revisions
fusion-lab/data.lua file | annotate | diff | comparison | revisions
fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-animation-1.png file | annotate | diff | comparison | revisions
fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-animation-2.png file | annotate | diff | comparison | revisions
fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-animation.lua file | annotate | diff | comparison | revisions
fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-emission-1.png file | annotate | diff | comparison | revisions
fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-emission-2.png file | annotate | diff | comparison | revisions
fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-emission.lua file | annotate | diff | comparison | revisions
fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-shadow.lua file | annotate | diff | comparison | revisions
fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-shadow.png file | annotate | diff | comparison | revisions
fusion-lab/graphics/entity/fusion-lab/photometric-lab-icon-big.png file | annotate | diff | comparison | revisions
fusion-lab/graphics/entity/fusion-lab/photometric-lab-preview1.png file | annotate | diff | comparison | revisions
fusion-lab/graphics/entity/fusion-lab/photometric-lab-preview2.png file | annotate | diff | comparison | revisions
fusion-lab/graphics/icons/photometric-lab-icon.png file | annotate | diff | comparison | revisions
fusion-lab/graphics/technology/fusion-lab.png file | annotate | diff | comparison | revisions
fusion-lab/info.json file | annotate | diff | comparison | revisions
fusion-lab/locale/en/fusion-lab.cfg file | annotate | diff | comparison | revisions
fusion-lab/locale/fi/fusion-lab.cfg file | annotate | diff | comparison | revisions
fusion-lab/locale/ja/fusion-lab.cfg file | annotate | diff | comparison | revisions
fusion-lab/menu-simulations/menu-simulation-fusion-lab.lua file | annotate | diff | comparison | revisions
fusion-lab/settings.lua file | annotate | diff | comparison | revisions
fusion-lab/thumbnail.png file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/changelog.txt	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,63 @@
+---------------------------------------------------------------------------------------------------
+Version: 1.2.3
+Date: 2025-02-27
+  Changes:
+    - No longer explodes spontaneously. Still sets on fire though and will explode if it dies while on fire.
+    - Explosion nerfed to multiple artillery explosions instead of atomic explosion.
+    - Now copies lab inputs from the primary  lab as well as the biolab
+  Fixes:
+    - Fixed sprite offsets
+
+---------------------------------------------------------------------------------------------------
+Version: 1.2.2
+Date: 2025-02-27
+  Features:
+    - Stats now configurable in settings.
+  Changes:
+    - Changed default stats; research speed = 3, drain = 40%, module slots = 6
+  Fixes:
+    - Fixed modded science packs not appearing as lab inputs
+
+---------------------------------------------------------------------------------------------------
+Version: 1.2.1
+Date: 2025-02-10
+  Fixes:
+    - Fix error on load with some science pack mods
+
+---------------------------------------------------------------------------------------------------
+Version: 1.2.0
+Date: 2025-02-09
+  Fixes:
+    - Improved UPS by optimizing runtime code
+  Changes:
+    - Increased lab health from 100 to 350
+
+---------------------------------------------------------------------------------------------------
+Version: 1.1.2
+Date: 2025-02-09
+  Fixes:
+    - Fix compatibility with Nuclear Science (and possibly some other mods)
+
+---------------------------------------------------------------------------------------------------
+Version: 1.1.1
+Date: 2025-02-08
+  Changes:
+    - Adjusted fusion lab volumes
+
+---------------------------------------------------------------------------------------------------
+Version: 1.1.0
+Date: 2025-02-08
+  Features:
+    - Added Hurricane's photometric lab graphics
+    - Added working sound, complete with sound accents to match the graphics
+    - Remade the custom menu simulation
+  Changes:
+    - Swapped selection boxes of the lab and the heat interface so that the lab selection box is 5×5
+
+---------------------------------------------------------------------------------------------------
+Version: 1.0.2
+Date: 2025-02-07
+  Features:
+    - Added locales
+  Changes:
+    - Menu simulation is now less dark
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/control.lua	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,159 @@
+local function init_storage()
+	if storage.fusion_labs == nil
+	then
+		storage.fusion_labs = {}
+		local num_labs = 0
+		for _, surface in pairs(game.surfaces)
+		do
+			for _, lab in pairs(surface.find_entities_filtered{name="fusion-lab"})
+			do
+				if lab.valid
+				then
+				num_labs = num_labs + 1
+					local heat_interfaces = surface.find_entities_filtered{
+						name='fusion-lab-heat-interface',
+						position=lab.position,
+						limit = 1,
+					}
+					local heat_interface
+					if #heat_interfaces == 0
+					then
+						heat_interfaces = {lab.surface.create_entity{
+							name='fusion-lab-heat-interface',
+							position=lab.position,
+							force=lab.force,
+						}}
+						heat_interfaces[1].temperature = 15
+						heat_interfaces[1].destructible = false
+					end
+					storage.fusion_labs[lab] = {
+						heat_interface = heat_interfaces[1],
+					}
+				end
+			end
+		end
+	end
+end
+
+local function cleanup_labs()
+	local new_storage_fusion_labs = {}
+	for lab, lab_info in pairs(storage.fusion_labs)
+	do
+		if lab.valid
+		then
+			new_storage_fusion_labs[lab] = lab_info
+		end
+	end
+	storage.fusion_labs = new_storage_fusion_labs
+end
+
+script.on_nth_tick(31, function(event)
+	init_storage()
+	local need_cleanup = false
+	for lab, lab_info in pairs(storage.fusion_labs)
+	do
+		if not lab.valid
+		then
+			need_cleanup = true
+		elseif lab.status == defines.entity_status.working
+		then
+			local heat_interface = lab_info.heat_interface
+			lab.minable = (heat_interface.temperature <= 900)
+			-- A cryogenic plant cooling hot fluoroketone voids 1320kW
+			-- of heat energy, so the fusion lab with no modules just happens
+			-- to conveniently radiate exactly half of that energy rate.
+			--
+			-- The 0.341 here is 31/60*0.66 = 0.341 degrees of heat increase
+			-- every 31 ticks, for 0.66 ℃/sec. it's written out as 0.341 to
+			-- avoid potential floating point rounding errors.
+			heat_interface.temperature = heat_interface.temperature + (1 + lab.consumption_bonus) * 0.341
+			if heat_interface.temperature > 600
+			then
+				lab.surface.create_entity{
+					name = 'fusion-lab-smoke-source',
+					position={lab.position.x + 3.5 * (math.random() - 0.5), lab.position.y + 3.5 * (math.random() - 0.5)},
+					force=lab.force,
+				}
+			end
+			if heat_interface.temperature > 710.0
+			then
+				lab.surface.create_entity{
+					name = 'crash-site-fire-flame',
+					position={lab.position.x + 3.5 * (math.random() - 0.5), lab.position.y + 3.5 * (math.random() - 0.5)},
+					force=game.forces.enemy,
+				}
+			end
+			--[[
+			if heat_interface.temperature > 750.0
+			then
+				lab.die()
+			end
+			]]--
+		end
+	end
+	if need_cleanup
+	then
+		cleanup_labs()
+	end
+end)
+
+
+script.on_event(defines.events.on_entity_died, function(event)
+	local lab = event.entity
+	-- we can't rely on storage here
+	local heat_interfaces = lab.surface.find_entities_filtered{
+		name='fusion-lab-heat-interface',
+		position=lab.position,
+		limit = 1,
+	}
+	if #heat_interfaces > 0
+	then
+		local heat_interface = heat_interfaces[1]
+		if heat_interface.temperature > 700
+		then
+			for _, ofs in pairs{{0, 0}, {5, 0}, {-5, 0}, {0, 5}, {0, -5}, {1.5, 1.5}, {-1.5, 1.5}, {-1.5, -1.5}, {1.5, -1.5}}
+			do
+				local pos = {x = lab.position.x + ofs[1], y = lab.position.y + ofs[2]}
+				lab.surface.create_entity{
+					name = 'artillery-projectile',
+					position = pos,
+					target = pos,
+					force = game.forces.enemy,
+				}
+			end
+		end
+		heat_interface.destroy()
+	end
+end, {{filter="name", name="fusion-lab"}})
+
+local function on_fusion_lab_mined(event)
+	init_storage()
+	local entity = event.entity
+	for _, heat_interface in pairs(entity.surface.find_entities_filtered{
+			name='fusion-lab-heat-interface',
+			position = entity.position
+		})
+	do
+		heat_interface.destroy()
+	end
+end
+
+local function on_fusion_lab_built(event)
+	init_storage()
+	local lab = event.entity
+	heat_interface = lab.surface.create_entity{
+		name='fusion-lab-heat-interface',
+		position=lab.position,
+		force=lab.force,
+	}
+	heat_interface.temperature = 15
+	heat_interface.destructible = false
+	storage.fusion_labs[lab] = {heat_interface = heat_interface}
+end
+
+script.on_event(defines.events.on_player_mined_entity, on_fusion_lab_mined, {{filter="name", name="fusion-lab"}})
+script.on_event(defines.events.on_robot_mined_entity, on_fusion_lab_mined, {{filter="name", name="fusion-lab"}})
+script.on_event(defines.events.on_space_platform_mined_entity, on_fusion_lab_mined, {{filter="name", name="fusion-lab"}})
+script.on_event(defines.events.on_built_entity, on_fusion_lab_built, {{filter="name", name="fusion-lab"}})
+script.on_event(defines.events.on_robot_built_entity, on_fusion_lab_built, {{filter="name", name="fusion-lab"}})
+script.on_event(defines.events.on_space_platform_built_entity, on_fusion_lab_built, {{filter="name", name="fusion-lab"}})
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/data-final-fixes.lua	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,17 @@
+-- make sure it can accept all science packs a biolab can, even if extra science packs are added
+-- and it turns out some mods also add new science packs to the regular lab and not the biolab,
+-- so we need to add inputs from both of them
+local inputs = {}
+for _, lab in pairs{data.raw.lab.lab, data.raw.lab.biolab}
+do
+	for _, lab_input in pairs(lab.inputs)
+	do
+		inputs[lab_input] = 1
+	end
+end
+
+data.raw.lab["fusion-lab"].inputs = {}
+for lab_input, _ in pairs(inputs)
+do
+	table.insert(data.raw.lab["fusion-lab"].inputs, lab_input)
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/data-updates.lua	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,17 @@
+-- Using fluoroketones in a heat exchanger requires both fluoroketones
+-- have the same default temperature, so we need to modify that here.
+data.raw.fluid["fluoroketone-hot"].default_temperature = data.raw.fluid["fluoroketone-cold"].default_temperature
+data.raw.fluid["fluoroketone-hot"].max_temperature = 180
+
+-- We now need need to adjust any recipes that output fluoroketone at default temperature.
+-- The cryogenic science pack is one.
+for _, recipe in pairs(data.raw.recipe)
+do
+	for _, result in pairs(recipe.results or {})
+	do
+		if result.type == 'fluid' and result.name == 'fluoroketone-hot' and result.temperature == nil
+		then
+			result.temperature = 180
+		end
+	end
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/data.lua	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,301 @@
+local fluoro_heat_exchanger_icons = {
+	{icon = data.raw.boiler['heat-exchanger'].icon},
+	{
+		icon = data.raw.fluid['fluoroketone-cold'].icon,
+		scale = 0.3,
+		shift = {-8, -8},
+	},
+}
+
+data:extend{
+	{
+		type = "item",
+		name = "fusion-lab",
+		icon = "__fusion-lab__/graphics/icons/photometric-lab-icon.png",
+		subgroup = "production-machine",
+		order = "z[lab]b[fusion-lab]",
+		inventory_move_sound = data.raw.lab.lab.inventory_move_sound,
+		pick_sound = data.raw.item["fusion-reactor"].pick_sound,
+		drop_sound = data.raw.item["fusion-reactor"].drop_sound,
+		place_result = "fusion-lab",
+		weight = 200 * kg,
+		stack_size = 5,
+		default_import_location = "aquilo"
+	},
+	{
+		type = "recipe",
+		name = "fusion-lab",
+		category = data.raw.recipe['fusion-reactor'].category,
+		subgroup = data.raw.recipe.lab.subgroup,
+		order = "x[fusion-lab]",
+		enabled = false,
+		energy_required = data.raw.recipe.biolab.energy_required,
+		ingredients =
+		{
+			{type = "item", name = "lab", amount = 1},
+			{type = "item", name = "refined-concrete", amount = 25},
+			{type = "item", name = "tungsten-plate", amount = 50},
+			{type = "item", name = "holmium-plate", amount = 50},
+			{type = "item", name = "quantum-processor", amount = 50},
+		},
+		results = {
+			{type = "item", name = "fusion-lab", amount = 1},
+		},
+		surface_conditions = table.deepcopy(data.raw.recipe['fusion-reactor'].surface_conditions),
+		allow_productivity = false,
+	},
+	{
+		type = "lab",
+		name = "fusion-lab",
+		selection_priority = 100,
+		icon = "__fusion-lab__/graphics/icons/photometric-lab-icon.png",
+		flags = {"placeable-player", "player-creation"},
+		minable = {mining_time = 0.2, result = "fusion-lab"},
+		max_health = 350,
+		corpse = "cargo-bay-remnants",
+		dying_explosion = "lab-explosion",
+		collision_box = data.raw.lab.biolab.collision_box, -- 5×5
+		selection_box = data.raw.lab.biolab.selection_box, -- 5×5
+		damaged_trigger_effect = data.raw.lab.lab.damaged_trigger_effect,
+		surface_conditions = table.deepcopy(data.raw.container['steel-chest'].surface_conditions),
+		on_animation =
+		{
+			layers =
+			{
+				util.sprite_load("__fusion-lab__/graphics/entity/fusion-lab/photometric-lab-hr-shadow", {
+					frame_count = 1,
+					scale = 0.5,
+					repeat_count = 80,
+					animation_speed = 0.4,
+					draw_as_shadow = true,
+				}),
+				util.sprite_load("__fusion-lab__/graphics/entity/fusion-lab/photometric-lab-hr-animation", {
+					frame_count = 80,
+					animation_speed = 0.4,
+					scale = 0.5
+				}),
+				util.sprite_load("__fusion-lab__/graphics/entity/fusion-lab/photometric-lab-hr-emission", {
+					frame_count = 80,
+					animation_speed = 0.4,
+					scale = 0.5,
+					blend_mode = "additive",
+					draw_as_glow = true,
+				}),
+			}
+		},
+		off_animation =
+		{
+			layers =
+			{
+				util.sprite_load("__fusion-lab__/graphics/entity/fusion-lab/photometric-lab-hr-shadow", {
+					frame_count = 1,
+					scale = 0.5,
+					repeat_count = 80,
+					animation_speed = 0.4,
+					draw_as_shadow = true,
+				}),
+				util.sprite_load("__fusion-lab__/graphics/entity/fusion-lab/photometric-lab-hr-animation", {
+					frame_count = 80,
+					animation_speed = 0.4,
+					scale = 0.5
+				}),
+			}
+		},
+		working_sound =
+		{
+			-- it's a space age ensemble!
+			sound =
+			{
+				-- filename = "__space-age__/sound/entity/fusion/fusion-reactor.ogg",
+				filename = "__space-age__/sound/entity/tesla-turret/tesla-turret-rotation-loop.ogg",
+				volume = 0.65,
+				max_sounds_per_prototype = 2,
+			},
+			fade_in_ticks = 4,
+			fade_out_ticks = 20,
+			sound_accents =
+			{
+				{sound = {filename = "__space-age__/sound/entity/foundry/foundry-slide-open.ogg", volume = 0.65, audible_distance_modifier = 0.3}, frame = 3},
+				{sound = {filename = "__space-age__/sound/entity/foundry/foundry-slide-close.ogg", volume = 0.65, audible_distance_modifier = 0.3}, frame = 34},
+				{sound = {variations = sound_variations("__quality__/sound/recycler/recycler-mechanic", 3, 0.85), audible_distance_modifier = 0.3}, frame = 16},
+				{sound = {variations = sound_variations("__quality__/sound/recycler/recycler-mechanic", 3, 0.85), audible_distance_modifier = 0.3}, frame = 41},
+				{sound = {variations = sound_variations("__quality__/sound/recycler/recycler-jaw-shut", 3, 0.35), audible_distance_modifier = 0.3}, frame = 24},
+				{sound = {variations = sound_variations("__quality__/sound/recycler/recycler-jaw-shut", 3, 0.45), audible_distance_modifier = 0.3}, frame = 64},
+				{sound = {variations = sound_variations("__space-age__/sound/entity/electromagnetic-plant/emp-electric", 5, 0.5), audible_distance_modifier = 0.4, }, frame = 6 - 5},
+				{sound = {variations = sound_variations("__space-age__/sound/entity/electromagnetic-plant/emp-electric", 5, 0.5), audible_distance_modifier = 0.4, }, frame = 56 - 5},
+				{sound = {filename = "__space-age__/sound/entity/tesla-turret/tesla-turret-rotation-stop.ogg", volume = 0.8, audible_distance_modifier = 0.6}, frame = 8},
+				{sound = {filename = "__space-age__/sound/entity/tesla-turret/tesla-turret-rotation-stop.ogg", volume = 0.8, audible_distance_modifier = 0.6}, frame = 44},
+				{sound = {variations = sound_variations("__space-age__/sound/entity/biolab/biolab-beaker", 7, 0.95), audible_distance_modifier = 0.6}, frame = 20},
+			},
+			max_sounds_per_prototype = 2
+		},
+		impact_category = "glass",
+		open_sound = { filename = "__base__/sound/open-close/lab-open.ogg", volume = 0.6 },
+		close_sound = { filename = "__base__/sound/open-close/lab-close.ogg", volume = 0.6 },
+		energy_source = table.deepcopy(data.raw['fusion-reactor']['fusion-reactor'].burner),
+		energy_usage = "1MW",
+		researching_speed = settings.startup["fusion-lab-researching-speed"].value,
+		module_slots = settings.startup["fusion-lab-module-slots"].value,
+		inputs = table.deepcopy(data.raw.lab.biolab.inputs),
+		science_pack_drain_rate_percent = settings.startup["fusion-lab-drain-rate-percent"].value,
+		icons_positioning = data.raw.lab.biolab.icons_positioning,
+	},
+	{
+		type = "heat-interface",
+		name = "fusion-lab-heat-interface",
+		icon = "__base__/graphics/icons/heat-interface.png",
+		flags = {"placeable-player", "not-on-map", "not-flammable", "not-blueprintable", "not-deconstructable"},
+		placeable_by = {item = "fusion-lab", count = 1},
+		collision_mask = {layers={}},
+		selection_priority = 150,
+		hidden = true,
+		factoriopedia_alternative = "fusion-lab",
+		max_health = 200,
+		corpse = "small-remnants",
+		collision_box = data.raw.lab.biolab.collision_box, -- 5×5
+		selection_box = data.raw.lab.lab.selection_box, -- 3×3
+		gui_mode = "none", -- all, none, admins
+		open_sound = data.raw["fusion-reactor"]["fusion-reactor"].open_sound,
+		close_sound = data.raw["fusion-reactor"]["fusion-reactor"].close_sound,
+		heat_buffer =
+		{
+			max_temperature = 1000,
+			specific_heat = "1MJ",
+			max_transfer = "1GW",
+			default_temperature = 15,
+			min_working_temperature = 15,
+			pipe_covers = data.raw.boiler["heat-exchanger"].energy_source.pipe_covers,
+			heat_pipe_covers = data.raw.boiler["heat-exchanger"].energy_source.heat_pipe_covers,
+			connections =
+			{
+				{position = { 2, -2}, direction = defines.direction.north},
+				{position = {-2, -2}, direction = defines.direction.north},
+				{position = { 2,	2}, direction = defines.direction.south},
+				{position = {-2,	2}, direction = defines.direction.south},
+				{position = { 2, -2}, direction = defines.direction.east},
+				{position = { 2,	2}, direction = defines.direction.east},
+				{position = {-2, -2}, direction = defines.direction.west},
+				{position = {-2,	2}, direction = defines.direction.west},
+			}
+		},
+		picture =
+		{
+			filename = "__base__/graphics/entity/nuclear-reactor/reactor-pipes.png",
+			height = 316,
+			width = 320,
+			scale = 0.5,
+			flags = {"no-crop"},
+			shift = util.by_pixel(-1, -5)
+		}
+	},
+	{
+		type = "item",
+		name = "fluoro-heat-exchanger",
+		icons = fluoro_heat_exchanger_icons,
+		subgroup = "energy",
+		order = "f[nuclear-energy]-d[fluoro-heat-exchanger]",
+		inventory_move_sound = data.raw.item["heat-exchanger"].inventory_move_sound,
+		pick_sound = data.raw.item["heat-exchanger"].pick_sound,
+		drop_sound = data.raw.item["heat-exchanger"].drop_sound,
+		place_result = "fluoro-heat-exchanger",
+		stack_size = 50,
+		weight = 40*kg,
+		random_tint_color = data.raw.item["heat-exchanger"].random_tint_color,
+	},
+	{
+		type = "technology",
+		name = "fusion-lab",
+		icon = "__fusion-lab__/graphics/technology/fusion-lab.png",
+		icon_size = 256,
+		effects =
+		{
+			{
+				type = "unlock-recipe",
+				recipe = "fusion-lab"
+			},
+			{
+				type = "unlock-recipe",
+				recipe = "fluoro-heat-exchanger"
+			},
+		},
+		prerequisites = {"fusion-reactor"},
+		unit =
+		{
+			count_formula = "1000",
+			ingredients = table.deepcopy(data.raw.technology["fusion-reactor"].unit.ingredients),
+			time = 60
+		}
+	},
+	{
+		type = "recipe",
+		name = "fluoro-heat-exchanger",
+		category = "crafting",
+		subgroup = data.raw.recipe["heat-exchanger"].subgroup,
+		order = "zzz[fluoro-heat-exchanger]",
+		enabled = false,
+		energy_required = data.raw.recipe["heat-exchanger"].energy_required,
+		ingredients = data.raw.recipe["heat-exchanger"].ingredients,
+		results = {
+			{type = "item", name = "fluoro-heat-exchanger", amount = 1},
+		},
+		allow_productivity = false,
+	},
+}
+
+local fluoro_heat_exchanger = table.deepcopy(data.raw.boiler["heat-exchanger"])
+fluoro_heat_exchanger.name = "fluoro-heat-exchanger"
+fluoro_heat_exchanger.icon = nil
+fluoro_heat_exchanger.icons = fluoro_heat_exchanger_icons
+fluoro_heat_exchanger.fluid_box.filter = "fluoroketone-cold"
+fluoro_heat_exchanger.output_fluid_box.filter = "fluoroketone-hot"
+fluoro_heat_exchanger.minable.result = "fluoro-heat-exchanger"
+fluoro_heat_exchanger.energy_source.min_working_temperature = 180
+fluoro_heat_exchanger.target_temperature = 180
+data:extend{fluoro_heat_exchanger}
+
+local smoke_source = table.deepcopy(data.raw["particle-source"]["nuclear-smouldering-smoke-source"])
+smoke_source.name = "fusion-lab-smoke-source"
+smoke_source.time_before_start = 0
+smoke_source.time_before_start_deviation = 0
+smoke_source.time_to_live = 31
+smoke_source.time_to_live_deviation = 0
+smoke_source.height = 0.8
+smoke_source.smoke[1].frequency = 0.5
+data:extend{smoke_source}
+
+-- TODO: replace the fusion-lab-heat-interface with this, once I figure out how
+-- to change its "consumption" (i.e. power output) during runtime...
+--[[
+local void_reactor = table.deepcopy(data.raw.reactor['nuclear-reactor'])
+void_reactor.name = "void-reactor"
+void_reactor.energy_source = {type = "void"}
+void_reactor.minable = nil
+void_reactor.consumption = "660kW"
+void_reactor.neighbour_bonus = 0
+void_reactor.surface_conditions = nil
+void_reactor.picture = nil
+void_reactor.light = nil
+void_reactor.working_light_picture = nil
+data:extend{void_reactor}
+]]--
+
+local fusion_lab_menu_simulation = {
+    checkboard = false,
+    save = '__fusion-lab__/menu-simulations/menu-simulation-fusion-lab.zip',
+    length = 60 * 14,
+    init =
+    [[
+      local sim_planet = game.surfaces.fulgora
+      local logo = sim_planet.find_entities_filtered{name = "factorio-logo-11tiles", limit = 1}[1]
+      logo.destructible = false
+      local center = {logo.position.x, logo.position.y+9.75}
+      game.simulation.camera_surface_index = sim_planet.index
+      game.simulation.camera_position = center
+      game.simulation.camera_zoom = 1
+      game.tick_paused = false
+      require("__fusion-lab__.menu-simulations.menu-simulation-fusion-lab")
+    ]]
+  }
+
+-- data.raw["utility-constants"]["default"].main_menu_simulations = {}
+data.raw["utility-constants"]["default"].main_menu_simulations["fusion-lab"] = fusion_lab_menu_simulation
Binary file fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-animation-1.png has changed
Binary file fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-animation-2.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-animation.lua	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,11 @@
+return {
+    width = 330,
+    height = 390,
+    line_length = 8,
+    filenames = {
+        "-1.png",
+        "-2.png"
+    },
+    lines_per_file = 8,
+    shift = util.by_pixel(0, -16)
+}
Binary file fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-emission-1.png has changed
Binary file fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-emission-2.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-emission.lua	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,11 @@
+return {
+    width = 330,
+    height = 390,
+    line_length = 8,
+    filenames = {
+        "-1.png",
+        "-2.png"
+    },
+    lines_per_file = 8,
+    shift = util.by_pixel(0, -16)
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-shadow.lua	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,9 @@
+return
+{
+  width = 466,
+  height = 350,
+  shift = util.by_pixel(30, -16),
+  line_length = 1,
+  filenames = {".png"},
+  lines_per_file = 1,
+}
Binary file fusion-lab/graphics/entity/fusion-lab/photometric-lab-hr-shadow.png has changed
Binary file fusion-lab/graphics/entity/fusion-lab/photometric-lab-icon-big.png has changed
Binary file fusion-lab/graphics/entity/fusion-lab/photometric-lab-preview1.png has changed
Binary file fusion-lab/graphics/entity/fusion-lab/photometric-lab-preview2.png has changed
Binary file fusion-lab/graphics/icons/photometric-lab-icon.png has changed
Binary file fusion-lab/graphics/technology/fusion-lab.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/info.json	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,10 @@
+{
+	"name": "fusion-lab",
+	"title": "Fusion lab",
+	"description": "A fusion-based alternative to the biolab that can be placed on any planet. Requires cooling to avoid science meltdown. Credit to Hurricane for the graphics.",
+	"version": "1.2.3",
+	"author": "teemu",
+	"factorio_version": "2.0",
+	"dependencies": ["base >= 2.0.32", "space-age >= 2.0.32"],
+	"space_travel_required": true
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/locale/en/fusion-lab.cfg	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,19 @@
+[entity-name]
+fusion-lab=Fusion lab
+fusion-lab-heat-interface=Fusion lab heat interface
+fluoro-heat-exchanger=Heat exchanger (fluoroketone)
+
+[entity-description]
+fusion-lab=Performs research using fusion power cells. Fusion science is volatile and requires cooling to avoid meltdown.\nGenerates 660kW of waste heat energy, which scales with energy consumption.\nRisks exploding if temperature reaches 700°C.
+fluoro-heat-exchanger=A variant of the heat exchanger that transfers heat to fluoroketone.
+
+[technology-name]
+fusion-lab=Fusion lab
+
+[technology-description]
+fusion-lab=Uses fusion power to perform research. Can be built on any planet. Requires cooling to avoid science meltdown.
+
+[mod-setting-name]
+fusion-lab-researching-speed=[entity=fusion-lab] Fusion lab researching speed
+fusion-lab-drain-rate-percent=[entity=fusion-lab] Fusion lab science pack drain (in percents)
+fusion-lab-module-slots=[entity=fusion-lab] Fusion lab module slots count
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/locale/fi/fusion-lab.cfg	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,19 @@
+[entity-name]
+fusion-lab=Fuusiolaboratorio
+fusion-lab-heat-interface=Fuusiolaboratorion lämpöputkisto
+fluoro-heat-exchanger=Lämmönsiirrin (fluoroketoni)
+
+[entity-description]
+fusion-lab=Tekee tutkimusta fuusiovoimakennoilla. Fuusiotiede on räjähdysherkkä ja vaatii jäähdytysjärjestelmän välttääkseen räjähtämisen.\nTuottaa 660kW hukkalämpöä, mikä skaalautuu energiankulutuksen kanssa.\nSaattaa räjähtää lämpötilan noustessa 700 asteeseen.
+fluoro-heat-exchanger=Lämmönsiirtimen muunnos, joka siirtää lämpöä fluoroketoniin.
+
+[technology-name]
+fusion-lab=Fuusiolaboratorio
+
+[technology-description]
+fusion-lab=Käyttää fuusioenergiaa tehdäkseen tutkimusta. Voidaan rakentaa mille tahansa planeetalle. Vaatii jäähdytysjärjestelmän välttääkseen räjähtämisen.
+
+[mod-setting-name]
+fusion-lab-researching-speed=[entity=fusion-lab] Fuusiolaboratorion tutkimusnopeus
+fusion-lab-drain-rate-percent=[entity=fusion-lab] Fuusiolaboratorion tiedepakettien kulutus (prosentteja)
+fusion-lab-module-slots=[entity=fusion-lab] Fuusiolaboratorion moduulipaikat
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/locale/ja/fusion-lab.cfg	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,19 @@
+[entity-name]
+fusion-lab=核融合研究所
+fusion-lab-heat-interface=核融合研究所の熱界面
+fluoro-heat-exchanger=熱交換器(フルオロケトン)
+
+[entity-description]
+fusion-lab=核融合燃料棒を用いて研究を行います。核融合研究は爆発しがちのため、冷却システムを築く必要があります。\n660kWの廃熱を発生しますが、この発熱量はエネルギー消費量とともに上がります。\n温度が700℃を超えると爆発するおそれがあります。
+fluoro-heat-exchanger=熱交換器のバリエーション。熱をヒートパイプからフルオロケトンに移します。
+
+[technology-name]
+fusion-lab=核融合研究所
+
+[technology-description]
+fusion-lab=核融合燃料棒を用いて研究を行います。すべての惑星において配置できます。科学爆発をしないように冷却が必要です。
+
+[mod-setting-name]
+fusion-lab-researching-speed=[entity=fusion-lab] 核融合研究所の研究速度
+fusion-lab-drain-rate-percent=[entity=fusion-lab] 核融合研究所のサイエンスパックの消費率(パーセント単位)
+fusion-lab-module-slots=[entity=fusion-lab] 核融合研究所のモジュールスロット数
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/menu-simulations/menu-simulation-fusion-lab.lua	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,38 @@
+require("__fusion-lab__.control")
+
+local sim_planet = game.surfaces.fulgora
+local tick = 0
+local heat_interfaces = {}
+
+for _, ent in pairs(sim_planet.find_entities_filtered{name = 'fusion-lab-heat-interface'})
+do
+	table.insert(heat_interfaces, ent)
+	ent.set_heat_setting{temperature=705, mode="exactly"}
+end
+
+-- compatibility for mods that add extra science packs (like nuclear science),
+-- we just cheat those into the labs to get them running
+for _, ent in pairs(sim_planet.find_entities_filtered{name = 'fusion-lab'})
+do
+	for _, science_pack in pairs(ent.prototype.lab_inputs)
+	do
+		if ent.get_item_count(science_pack) == 0
+		then
+			ent.insert{name = science_pack, count = prototypes.item[science_pack].stack_size}
+		end
+	end
+end
+
+script.on_nth_tick(1, function()
+	tick = tick + 1
+	if tick >= 240
+	then
+		for _, ent in pairs(heat_interfaces)
+		do
+			if math.random() > 0.95
+			then
+				ent.set_heat_setting{temperature=715, mode="exactly"}
+			end
+		end
+	end
+end)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/fusion-lab/settings.lua	Wed Jul 02 14:28:57 2025 +0300
@@ -0,0 +1,23 @@
+data:extend({
+    {
+        type = "double-setting",
+        name = "fusion-lab-researching-speed",
+        setting_type = "startup",
+        default_value = 3,
+        order = "a",
+    },
+    {
+        type = "int-setting",
+        name = "fusion-lab-drain-rate-percent",
+        setting_type = "startup",
+        default_value = 40,
+        order = "b",
+    },
+    {
+        type = "int-setting",
+        name = "fusion-lab-module-slots",
+        setting_type = "startup",
+        default_value = 6,
+        order = "c",
+    }
+})
Binary file fusion-lab/thumbnail.png has changed

mercurial