2 function getConfig(force, config_changed)
3 if not storage.auto_research_config then
4 storage.auto_research_config = {}
7 -- Disable Research Queue popup
8 if remote.interfaces.RQ and remote.interfaces.RQ["popup"] then
9 remote.call("RQ", "popup", false)
12 if not storage.auto_research_config[force.name] then
13 storage.auto_research_config[force.name] = {
14 prioritized_techs = {}, -- "prioritized" is "queued". kept for backwards compatability (because i'm lazy and don't want migration code)
15 deprioritized_techs = {} -- "deprioritized" is "blacklisted". kept for backwards compatability (because i'm lazy and don't want migration code)
17 -- Enable Auto Research
18 setAutoResearch(force, true)
20 -- Disable queued only
21 setQueuedOnly(force, false)
23 -- Allow switching research
24 setAllowSwitching(force, true)
26 -- Print researched technology
27 setAnnounceCompletedResearch(force, true)
30 -- set research strategy
31 storage.auto_research_config[force.name].research_strategy = storage.auto_research_config[force.name].research_strategy or "balanced"
33 if config_changed or not storage.auto_research_config[force.name].allowed_ingredients or not storage.auto_research_config[force.name].infinite_research then
34 -- remember any old ingredients
35 local old_ingredients = {}
36 if storage.auto_research_config[force.name].allowed_ingredients then
37 for name, enabled in pairs(storage.auto_research_config[force.name].allowed_ingredients) do
38 old_ingredients[name] = enabled
41 -- find all possible tech ingredients
42 -- also scan for research that are infinite: techs that have no successor and tech.research_unit_count_formula is not nil
43 storage.auto_research_config[force.name].allowed_ingredients = {}
44 storage.auto_research_config[force.name].infinite_research = {}
45 local finite_research = {}
46 for _, tech in pairs(force.technologies) do
47 for _, ingredient in pairs(tech.research_unit_ingredients) do
48 storage.auto_research_config[force.name].allowed_ingredients[ingredient.name] = (old_ingredients[ingredient.name] == nil or old_ingredients[ingredient.name])
50 if tech.research_unit_count_formula then
51 storage.auto_research_config[force.name].infinite_research[tech.name] = tech
53 for _, pretech in pairs(tech.prerequisites) do
54 if pretech.enabled and not pretech.researched then
55 finite_research[pretech.name] = true
59 for techname, _ in pairs(finite_research) do
60 storage.auto_research_config[force.name].infinite_research[techname] = nil
64 return storage.auto_research_config[force.name]
67 function setAutoResearch(force, enabled)
71 local config = getConfig(force)
72 config.enabled = enabled
76 startNextResearch(force)
80 function setQueuedOnly(force, enabled)
84 getConfig(force).prioritized_only = enabled
87 startNextResearch(force)
90 function setAllowSwitching(force, enabled)
94 getConfig(force).allow_switching = enabled
97 startNextResearch(force)
100 function setAnnounceCompletedResearch(force, enabled)
104 getConfig(force).announce_completed = enabled
107 function setDeprioritizeInfiniteTech(force, enabled)
111 getConfig(force).deprioritize_infinite_tech = enabled
113 -- start new research
114 startNextResearch(force)
117 function getPretechs(tech)
119 pretechs[#pretechs + 1] = tech
121 while (index <= #pretechs) do
122 for _, pretech in pairs(pretechs[index].prerequisites) do
123 if pretech.enabled and not pretech.researched then
124 pretechs[#pretechs + 1] = pretech
132 function canResearch(force, tech, config)
133 if not tech or tech.researched or not tech.enabled or tech.prototype.hidden then
136 for _, pretech in pairs(tech.prerequisites) do
137 if not pretech.researched then
141 if #tech.research_unit_ingredients == 0 then
144 for _, ingredient in pairs(tech.research_unit_ingredients) do
145 if not config.allowed_ingredients[ingredient.name] then
149 for _, deprioritized in pairs(config.deprioritized_techs) do
150 if tech.name == deprioritized then
157 function startNextResearch(force, override_spam_detection)
158 local config = getConfig(force)
159 if not config.enabled or (force.current_research and not config.allow_switching) or (not override_spam_detection and config.last_research_finish_tick == game.tick) then
162 config.last_research_finish_tick = game.tick -- if multiple research finish same tick for same force, the user probably enabled all techs
164 -- function for calculating tech effort
165 local calcEffort = function(tech)
166 local ingredientCount = function(ingredients)
167 local tech_ingredients = 0
168 for _, ingredient in pairs(tech.research_unit_ingredients) do
169 tech_ingredients = tech_ingredients + ingredient.amount
171 return tech_ingredients
174 if config.research_strategy == "fast" then
175 effort = math.max(tech.research_unit_energy, 1) * math.max(tech.research_unit_count, 1)
176 elseif config.research_strategy == "slow" then
177 effort = math.max(tech.research_unit_energy, 1) * math.max(tech.research_unit_count, 1) * -1
178 elseif config.research_strategy == "cheap" then
179 effort = math.max(ingredientCount(tech.research_unit_ingredients), 1) * math.max(tech.research_unit_count, 1)
180 elseif config.research_strategy == "expensive" then
181 effort = math.max(ingredientCount(tech.research_unit_ingredients), 1) * math.max(tech.research_unit_count, 1) * -1
182 elseif config.research_strategy == "balanced" then
183 effort = math.max(tech.research_unit_count, 1) * math.max(tech.research_unit_energy, 1) * math.max(ingredientCount(tech.research_unit_ingredients), 1)
185 effort = math.random(1, 999)
187 if (config.deprioritize_infinite_tech and config.infinite_research[tech.name]) then
188 return effort * (effort > 0 and 1000 or -1000)
194 -- see if there are some techs we should research first
195 local next_research = nil
196 local least_effort = nil
197 for _, techname in pairs(config.prioritized_techs) do
198 local tech = force.technologies[techname]
199 if tech and not next_research then
200 local pretechs = getPretechs(tech)
201 for _, pretech in pairs(pretechs) do
202 local effort = calcEffort(pretech)
203 if (not least_effort or effort < least_effort) and canResearch(force, pretech, config) then
204 next_research = pretech.name
205 least_effort = effort
211 -- if no queued tech should be researched then research the "least effort" tech not researched yet
212 if not config.prioritized_only and not next_research then
213 for techname, tech in pairs(force.technologies) do
214 if tech.enabled and not tech.researched then
215 local effort = calcEffort(tech)
216 if (not least_effort or effort < least_effort) and canResearch(force, tech, config) then
217 next_research = techname
218 least_effort = effort
224 -- keep queue, just put next_research at the start.
225 if next_research then
228 -- manage last element of research queue
230 if force.research_queue[i] == nil then break end
231 if i == (#force.research_queue) then
232 if not (force.current_research and config.allow_switching) then
233 table.insert(rq, force.research_queue[i].name)
236 table.insert(rq, force.research_queue[i].name)
239 table.insert(rq, next_research)
241 force.research_queue = rq
245 function onResearchFinished(event)
246 local force = event.research.force
247 local config = getConfig(force)
248 -- remove researched stuff from prioritized_techs and deprioritized_techs
249 for i = #config.prioritized_techs, 1, -1 do
250 local tech = force.technologies[config.prioritized_techs[i]]
251 if not tech or tech.researched then
252 table.remove(config.prioritized_techs, i)
255 for i = #config.deprioritized_techs, 1, -1 do
256 local tech = force.technologies[config.deprioritized_techs[i]]
257 if not tech or tech.researched then
258 table.remove(config.deprioritized_techs, i)
261 -- announce completed research
262 if config.announce_completed and config.no_announce_this_tick ~= game.tick then
263 if config.last_research_finish_tick == game.tick then
264 config.no_announce_this_tick = game.tick
267 if event.research.research_unit_count_formula then
268 level = (event.research.researched and event.research.level) or (event.research.level - 1)
270 force.print{"auto_research.announce_completed", event.research.localised_name, level}
274 startNextResearch(event.research.force)
279 toggleGui = function(player)
280 if player.gui.top.auto_research_gui then
281 player.gui.top.auto_research_gui.destroy()
283 local force = player.force
284 local config = getConfig(force)
285 local frame = player.gui.top.add{
287 name = "auto_research_gui",
288 direction = "vertical",
289 caption = {"auto_research_gui.title"}
291 local frameflow = frame.add{
293 style = "auto_research_list_flow",
295 direction = "vertical"
299 frameflow.add{type = "checkbox", name = "auto_research_enabled", caption = {"auto_research_gui.enabled"}, tooltip = {"auto_research_gui.enabled_tooltip"}, state = config.enabled or false}
300 frameflow.add{type = "checkbox", name = "auto_research_queued_only", caption = {"auto_research_gui.prioritized_only"}, tooltip = {"auto_research_gui.prioritized_only_tooltip"}, state = config.prioritized_only or false}
301 frameflow.add{type = "checkbox", name = "auto_research_allow_switching", caption = {"auto_research_gui.allow_switching"}, tooltip = {"auto_research_gui.allow_switching_tooltip"}, state = config.allow_switching or false}
302 frameflow.add{type = "checkbox", name = "auto_research_announce_completed", caption = {"auto_research_gui.announce_completed"}, tooltip = {"auto_research_gui.announce_completed_tooltip"}, state = config.announce_completed or false}
303 frameflow.add{type = "checkbox", name = "auto_research_deprioritize_infinite_tech", caption = {"auto_research_gui.deprioritize_infinite_tech"}, tooltip = {"auto_research_gui.deprioritize_infinite_tech_tooltip"}, state = config.deprioritize_infinite_tech or false}
308 style = "auto_research_header_label",
309 caption = {"auto_research_gui.research_strategy"}
311 local research_strategies_outer = frameflow.add{
313 style = "auto_research_tech_flow",
314 name = "research_strategies_outer",
315 direction = "horizontal"
317 research_strategies_outer.add{type = "radiobutton", name = "auto_research_research_fast", caption = {"auto_research_gui.research_fast"}, tooltip = {"auto_research_gui.research_fast_tooltip"}, state = config.research_strategy == "fast"}
318 research_strategies_outer.add{type = "radiobutton", name = "auto_research_research_slow", caption = {"auto_research_gui.research_slow"}, tooltip = {"auto_research_gui.research_slow_tooltip"}, state = config.research_strategy == "slow"}
319 research_strategies_outer.add{type = "radiobutton", name = "auto_research_research_cheap", caption = {"auto_research_gui.research_cheap"}, tooltip = {"auto_research_gui.research_cheap_tooltip"}, state = config.research_strategy == "cheap"}
320 research_strategies_outer.add{type = "radiobutton", name = "auto_research_research_expensive", caption = {"auto_research_gui.research_expensive"}, tooltip = {"auto_research_gui.research_expensive_tooltip"}, state = config.research_strategy == "expensive"}
321 research_strategies_outer.add{type = "radiobutton", name = "auto_research_research_balanced", caption = {"auto_research_gui.research_balanced"}, tooltip = {"auto_research_gui.research_balanced_tooltip"}, state = config.research_strategy == "balanced"}
322 research_strategies_outer.add{type = "radiobutton", name = "auto_research_research_random", caption = {"auto_research_gui.research_random"}, tooltip = {"auto_research_gui.research_random_tooltip"}, state = config.research_strategy == "random"}
324 research_strategies_outer.style.horizontal_spacing = 6
326 -- allowed ingredients
329 style = "auto_research_header_label",
330 caption = {"auto_research_gui.allowed_ingredients_label"}
332 local allowed_ingredients = frameflow.add{
334 style = "auto_research_list_flow",
335 name = "allowed_ingredients",
336 direction = "vertical"
338 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
343 style = "auto_research_header_label",
344 caption = {"auto_research_gui.prioritized_label"}
346 local prioritized = frameflow.add{
347 type = "scroll-pane",
348 name = "prioritized",
349 horizontal_scroll_policy = "never",
350 vertical_scroll_policy = "auto"
352 prioritized.style.top_padding = 5
353 prioritized.style.bottom_padding = 5
354 prioritized.style.maximal_height = 127
355 prioritized.style.minimal_width = 440
356 -- draw prioritized tech list
357 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
359 -- deprioritized techs
362 style = "auto_research_header_label",
363 caption = {"auto_research_gui.deprioritized_label"}
365 local deprioritized = frameflow.add{
366 type = "scroll-pane",
367 name = "deprioritized",
368 horizontal_scroll_policy = "never",
369 vertical_scroll_policy = "auto"
371 deprioritized.style.top_padding = 5
372 deprioritized.style.bottom_padding = 5
373 deprioritized.style.maximal_height = 127
374 deprioritized.style.minimal_width = 440
376 -- draw deprioritized tech list
377 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
380 local searchflow = frameflow.add{
383 style = "auto_research_tech_flow",
384 direction = "horizontal"
388 style = "auto_research_header_label",
389 caption = {"auto_research_gui.search_label"}
393 name = "auto_research_search_text",
394 tooltip = {"auto_research_gui.search_tooltip"}
398 name = "auto_research_ingredients_filter_search_results",
399 caption = {"auto_research_gui.ingredients_filter_search_results"},
400 tooltip = {"auto_research_gui.ingredients_filter_search_results_tooltip"},
401 state = config.filter_search_results or false
403 searchflow.style.horizontal_spacing = 6
404 searchflow.style.vertical_align = "center"
406 local search = frameflow.add{
407 type = "scroll-pane",
409 horizontal_scroll_policy = "never",
410 vertical_scroll_policy = "auto"
412 search.style.top_padding = 5
413 search.style.bottom_padding = 5
414 search.style.maximal_height = 127
415 search.style.minimal_width = 440
417 -- draw search result list
418 gui.updateSearchResult(player, "")
422 onCheckboxClick = function(event)
423 local player = game.players[event.player_index]
424 local force = player.force
425 local name = event.element.name
426 if name == "auto_research_enabled" then
427 setAutoResearch(force, event.element.state)
428 elseif name == "auto_research_queued_only" then
429 setQueuedOnly(force, event.element.state)
430 elseif name == "auto_research_allow_switching" then
431 setAllowSwitching(force, event.element.state)
432 elseif name == "auto_research_announce_completed" then
433 setAnnounceCompletedResearch(force, event.element.state)
434 elseif name == "auto_research_deprioritize_infinite_tech" then
435 setDeprioritizeInfiniteTech(force, event.element.state)
436 elseif name == "auto_research_ingredients_filter_search_results" then
437 local config = getConfig(force)
438 config.filter_search_results = event.element.state
439 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
443 onClick = function(event)
444 local player = game.players[event.player_index]
445 local force = player.force
446 local config = getConfig(force)
447 local name = event.element.name
448 if name == "auto_research_search_text" then
449 if event.button == defines.mouse_button_type.right then
450 player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text = ""
451 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
453 elseif string.find(name, "auto_research_research") then
454 config.research_strategy = string.match(name, "^auto_research_research_(.*)$")
455 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_fast.state = (config.research_strategy == "fast")
456 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_cheap.state = (config.research_strategy == "cheap")
457 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_balanced.state = (config.research_strategy == "balanced")
458 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_slow.state = (config.research_strategy == "slow")
459 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_expensive.state = (config.research_strategy == "expensive")
460 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_random.state = (config.research_strategy == "random")
461 -- start new research
462 startNextResearch(force)
464 local prefix, name = string.match(name, "^auto_research_([^-]*)-(.*)$")
465 if prefix == "allow_ingredient" then
466 config.allowed_ingredients[name] = not config.allowed_ingredients[name]
467 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
468 if player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state then
469 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
471 startNextResearch(force)
472 elseif name and force.technologies[name] then
473 -- remove tech from prioritized list
474 for i = #config.prioritized_techs, 1, -1 do
475 if config.prioritized_techs[i] == name then
476 table.remove(config.prioritized_techs, i)
479 -- and from deprioritized list
480 for i = #config.deprioritized_techs, 1, -1 do
481 if config.deprioritized_techs[i] == name then
482 table.remove(config.deprioritized_techs, i)
485 if prefix == "queue_top" then
486 -- add tech to top of prioritized list
487 table.insert(config.prioritized_techs, 1, name)
488 elseif prefix == "queue_bottom" then
489 -- add tech to bottom of prioritized list
490 table.insert(config.prioritized_techs, name)
491 elseif prefix == "blacklist" then
492 -- add tech to list of deprioritized techs
493 table.insert(config.deprioritized_techs, name)
495 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
496 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
497 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
499 -- start new research
500 startNextResearch(force)
505 updateAllowedIngredientsList = function(flow, player, config)
507 while flow["flow" .. counter] do
508 flow["flow" .. counter].destroy()
509 counter = counter + 1
512 for ingredientname, allowed in pairs(config.allowed_ingredients) do
513 local flowname = "flow" .. math.floor(counter / 10) + 1
514 local ingredientflow = flow[flowname]
515 if not ingredientflow then
516 ingredientflow = flow.add {
518 style = "auto_research_tech_flow",
520 direction = "horizontal"
523 local sprite = "auto_research_tool_" .. ingredientname
524 if not helpers.is_valid_sprite_path(sprite) then
525 sprite = "auto_research_unknown"
527 ingredientflow.add{type = "sprite-button", style = "auto_research_sprite_button_toggle" .. (allowed and "_pressed" or ""), name = "auto_research_allow_ingredient-" .. ingredientname, tooltip = {"item-name." .. ingredientname}, sprite = sprite}
528 counter = counter + 1
532 updateTechnologyList = function(scrollpane, technologies, player, show_queue_buttons)
533 if scrollpane.flow then
534 scrollpane.flow.destroy()
536 local flow = scrollpane.add{
538 style = "auto_research_list_flow",
540 direction = "vertical"
542 if #technologies > 0 then
543 for _, techname in pairs(technologies) do
544 local tech = player.force.technologies[techname]
546 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
547 if show_queue_buttons then
548 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. techname, sprite = "auto_research_prioritize_top"}
549 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. techname, sprite = "auto_research_prioritize_bottom"}
551 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_delete-" .. techname, sprite = "auto_research_delete"}
552 entryflow.add{type = "label", style = "auto_research_tech_label", caption = tech.localised_name}
553 for _, ingredient in pairs(tech.research_unit_ingredients) do
554 local sprite = "auto_research_tool_" .. ingredient.name
555 if not helpers.is_valid_sprite_path(sprite) then
556 sprite = "auto_research_unknown"
558 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
563 local entryflow = flow.add{type = "flow", direction = "horizontal"}
564 entryflow.add{type = "label", caption = {"auto_research_gui.none"}}
568 updateSearchResult = function(player, text)
569 local scrollpane = player.gui.top.auto_research_gui.flow.search
570 if scrollpane.flow then
571 scrollpane.flow.destroy()
573 local flow = scrollpane.add{
575 style = "auto_research_list_flow",
577 direction = "vertical"
579 local ingredients_filter = player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state
580 local config = getConfig(player.force)
582 text = string.lower(text)
583 -- NOTICE: localised name matching does not work at present, pending unlikely changes to Factorio API
584 for name, tech in pairs(player.force.technologies) do
585 if not tech.researched and tech.enabled and #tech.research_unit_ingredients > 0 then
586 local showtech = false
587 if string.find(string.lower(name), text, 1, true) then
588 -- show techs that match by name
590 -- elseif string.find(string.lower(game.technology_prototypes[name].localised_name), text, 1, true) then
591 -- -- show techs that match by localised name
594 for _, effect in pairs(tech.prototype.effects) do
595 if string.find(effect.type, text, 1, true) then
596 -- show techs that match by effect type
598 elseif effect.type == "unlock-recipe" then
599 if string.find(effect.recipe, text, 1, true) then
600 -- show techs that match by unlocked recipe name
602 -- elseif string.find(string.lower(game.recipe_prototypes[effect.recipe].localised_name), text, 1, true) then
603 -- -- show techs that match by unlocked recipe localised name
606 for _, product in pairs(prototypes.recipe[effect.recipe].products) do
607 if string.find(product.name, text, 1, true) then
608 -- show techs that match by unlocked recipe product name
610 -- elseif string.find(string.lower(game.item_prototypes[product.name].localised_name), text, 1, true) then
611 -- -- show techs that match by unlocked recipe product localised name
614 local prototype = prototypes.item[product.name]
616 if prototype.place_result then
617 if string.find(prototype.place_result.name, text, 1, true) then
618 -- show techs that match by unlocked recipe product placed entity name
620 -- elseif string.find(string.lower(game.entity_prototypes[prototype.place_result.name].localised_name), text, 1, true) then
621 -- -- show techs that match by unlocked recipe product placed entity localised name
624 elseif prototype.place_as_equipment_result then
625 if string.find(prototype.place_as_equipment_result.name, text, 1, true) then
626 -- show techs that match by unlocked recipe product placed equipment name
628 -- elseif string.find(string.lower(game.equipment_prototypes[prototype.place_as_equipment_result.name].localised_name), text, 1, true) then
629 -- -- show techs that match by unlocked recipe product placed equipment localised name
632 elseif prototype.place_as_tile_result then
633 if string.find(prototype.place_as_tile_result.result.name, text, 1, true) then
634 -- show techs that match by unlocked recipe product placed tile name
636 -- elseif string.find(string.lower(prototype.place_as_tile_result.result.localised_name), text, 1, true) then
637 -- -- show techs that match by unlocked recipe product placed tile localised name
648 if showtech and config.prioritized_techs then
649 for _, queued_tech in pairs(config.prioritized_techs) do
650 if name == queued_tech then
656 if showtech and config.deprioritized_techs then
657 for _, blacklisted_tech in pairs(config.deprioritized_techs) do
658 if name == blacklisted_tech then
664 if showtech and ingredients_filter then
665 for _, ingredient in pairs(tech.research_unit_ingredients) do
666 if not config.allowed_ingredients[ingredient.name] then
667 -- filter out techs that require disallowed ingredients (optional)
674 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
675 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. name, sprite = "auto_research_prioritize_top"}
676 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. name, sprite = "auto_research_prioritize_bottom"}
677 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_blacklist-" .. name, sprite = "auto_research_deprioritize"}
678 entryflow.add{type = "label", style = "auto_research_tech_label", name = name, caption = tech.localised_name}
679 for _, ingredient in pairs(tech.research_unit_ingredients) do
680 local sprite = "auto_research_tool_" .. ingredient.name
681 if not helpers.is_valid_sprite_path(sprite) then
682 sprite = "auto_research_unknown"
684 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
693 script.on_configuration_changed(function()
694 for _, force in pairs(game.forces) do
695 getConfig(force, true) -- triggers initialization of force config
698 script.on_event(defines.events.on_player_created, function(event)
699 local force = game.players[event.player_index].force
700 local config = getConfig(force) -- triggers initialization of force config
701 -- set any default queued/blacklisted techs
702 local queued_tech = settings.get_player_settings(game.players[event.player_index])["queued-tech-setting"].value
703 for tech in string.gmatch(queued_tech, "[^,$]+") do
704 tech = string.gsub(tech, "%s+", "")
705 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
706 table.insert(config.prioritized_techs, tech)
709 local blacklisted_tech = settings.get_player_settings(game.players[event.player_index])["blacklisted-tech-setting"].value
710 for tech in string.gmatch(blacklisted_tech, "[^,$]+") do
711 tech = string.gsub(tech, "%s+", "")
712 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
713 table.insert(config.deprioritized_techs, tech)
716 startNextResearch(force, true)
718 script.on_event(defines.events.on_force_created, function(event)
719 getConfig(event.force) -- triggers initialization of force config
721 script.on_event(defines.events.on_research_finished, onResearchFinished)
722 script.on_event(defines.events.on_gui_checked_state_changed, gui.onCheckboxClick)
723 script.on_event(defines.events.on_gui_click, gui.onClick)
724 script.on_event(defines.events.on_gui_text_changed, function(event)
725 if event.element.name ~= "auto_research_search_text" then
728 gui.updateSearchResult(game.players[event.player_index], event.element.text)
732 script.on_event("auto_research_toggle", function(event)
733 local player = game.players[event.player_index]
734 gui.toggleGui(player)
737 -- Add remote interfaces for enabling/disabling Auto Research
738 remote.add_interface("auto_research", {
739 enabled = function(forcename, value) setAutoResearch(game.forces[forcename], value) end,
740 queued_only = function(forcename, value) setQueuedOnly(game.forces[forcename], value) end,
741 allow_switching = function(forcename, value) setAllowSwitching(game.forces[forcename], value) end,
742 announce_completed = function(forcename, value) setAnnounceCompletedResearch(game.forces[forcename], value) end,
743 deprioritize_infinite_tech = function(forcename, value) setDeprioritizeInfiniteTech(game.forces[forcename], value) end