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 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