1 function getConfig(force, config_changed)
2 if not global.auto_research_config then
3 global.auto_research_config = {}
5 -- Disable Research Queue popup
6 if remote.interfaces.RQ and remote.interfaces.RQ["popup"] then
7 remote.call("RQ", "popup", false)
11 if not global.auto_research_config[force.name] then
12 global.auto_research_config[force.name] = {
13 prioritized_techs = {}, -- "prioritized" is "queued". kept for backwards compatability (because i'm lazy and don't want migration code)
14 deprioritized_techs = {} -- "deprioritized" is "blacklisted". kept for backwards compatability (because i'm lazy and don't want migration code)
16 -- Enable Auto Research
17 setAutoResearch(force, true)
19 -- Disable queued only
20 setQueuedOnly(force, false)
22 -- Allow switching research
23 setAllowSwitching(force, true)
25 -- Print researched technology
26 setAnnounceCompletedResearch(force, true)
29 -- set research strategy
30 global.auto_research_config[force.name].research_strategy = global.auto_research_config[force.name].research_strategy or "balanced"
32 if config_changed or not global.auto_research_config[force.name].allowed_ingredients or not global.auto_research_config[force.name].infinite_research then
33 -- remember any old ingredients
34 local old_ingredients = {}
35 if global.auto_research_config[force.name].allowed_ingredients then
36 for name, enabled in pairs(global.auto_research_config[force.name].allowed_ingredients) do
37 old_ingredients[name] = enabled
40 -- find all possible tech ingredients
41 -- also scan for research that are infinite: techs that have no successor and tech.research_unit_count_formula is not nil
42 global.auto_research_config[force.name].allowed_ingredients = {}
43 global.auto_research_config[force.name].infinite_research = {}
44 local finite_research = {}
45 for _, tech in pairs(force.technologies) do
46 for _, ingredient in pairs(tech.research_unit_ingredients) do
47 global.auto_research_config[force.name].allowed_ingredients[ingredient.name] = (old_ingredients[ingredient.name] == nil or old_ingredients[ingredient.name])
49 if tech.research_unit_count_formula then
50 global.auto_research_config[force.name].infinite_research[tech.name] = tech
52 for _, pretech in pairs(tech.prerequisites) do
53 if pretech.enabled and not pretech.researched then
54 finite_research[pretech.name] = true
58 for techname, _ in pairs(finite_research) do
59 global.auto_research_config[force.name].infinite_research[techname] = nil
63 return global.auto_research_config[force.name]
66 function setAutoResearch(force, enabled)
70 local config = getConfig(force)
71 config.enabled = enabled
72 force.research_queue_enabled = not 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 then
136 for _, pretech in pairs(tech.prerequisites) do
137 if not pretech.researched then
141 for _, ingredient in pairs(tech.research_unit_ingredients) do
142 if not config.allowed_ingredients[ingredient.name] then
146 for _, deprioritized in pairs(config.deprioritized_techs) do
147 if tech.name == deprioritized then
154 function startNextResearch(force, override_spam_detection)
155 local config = getConfig(force)
156 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
159 config.last_research_finish_tick = game.tick -- if multiple research finish same tick for same force, the user probably enabled all techs
161 -- function for calculating tech effort
162 local calcEffort = function(tech)
163 local ingredientCount = function(ingredients)
164 local tech_ingredients = 0
165 for _, ingredient in pairs(tech.research_unit_ingredients) do
166 tech_ingredients = tech_ingredients + ingredient.amount
168 return tech_ingredients
171 if config.research_strategy == "fast" then
172 effort = math.max(tech.research_unit_energy, 1) * math.max(tech.research_unit_count, 1)
173 elseif config.research_strategy == "slow" then
174 effort = math.max(tech.research_unit_energy, 1) * math.max(tech.research_unit_count, 1) * -1
175 elseif config.research_strategy == "cheap" then
176 effort = math.max(ingredientCount(tech.research_unit_ingredients), 1) * math.max(tech.research_unit_count, 1)
177 elseif config.research_strategy == "expensive" then
178 effort = math.max(ingredientCount(tech.research_unit_ingredients), 1) * math.max(tech.research_unit_count, 1) * -1
179 elseif config.research_strategy == "balanced" then
180 effort = math.max(tech.research_unit_count, 1) * math.max(tech.research_unit_energy, 1) * math.max(ingredientCount(tech.research_unit_ingredients), 1)
182 effort = math.random(1, 999)
184 if (config.deprioritize_infinite_tech and config.infinite_research[tech.name]) then
185 return effort * (effort > 0 and 1000 or -1000)
191 -- see if there are some techs we should research first
192 local next_research = nil
193 local least_effort = nil
194 for _, techname in pairs(config.prioritized_techs) do
195 local tech = force.technologies[techname]
196 if tech and not next_research then
197 local pretechs = getPretechs(tech)
198 for _, pretech in pairs(pretechs) do
199 local effort = calcEffort(pretech)
200 if (not least_effort or effort < least_effort) and canResearch(force, pretech, config) then
201 next_research = pretech.name
202 least_effort = effort
208 -- if no queued tech should be researched then research the "least effort" tech not researched yet
209 if not config.prioritized_only and not next_research then
210 for techname, tech in pairs(force.technologies) do
211 if tech.enabled and not tech.researched then
212 local effort = calcEffort(tech)
213 if (not least_effort or effort < least_effort) and canResearch(force, tech, config) then
214 next_research = techname
215 least_effort = effort
221 if next_research then
222 force.add_research(next_research)
226 function onResearchFinished(event)
227 local force = event.research.force
228 local config = getConfig(force)
229 -- remove researched stuff from prioritized_techs and deprioritized_techs
230 for i = #config.prioritized_techs, 1, -1 do
231 local tech = force.technologies[config.prioritized_techs[i]]
232 if not tech or tech.researched then
233 table.remove(config.prioritized_techs, i)
236 for i = #config.deprioritized_techs, 1, -1 do
237 local tech = force.technologies[config.deprioritized_techs[i]]
238 if not tech or tech.researched then
239 table.remove(config.deprioritized_techs, i)
242 -- announce completed research
243 if config.announce_completed and config.no_announce_this_tick ~= game.tick then
244 if config.last_research_finish_tick == game.tick then
245 config.no_announce_this_tick = game.tick
248 if event.research.research_unit_count_formula then
249 level = (event.research.researched and event.research.level) or (event.research.level - 1)
251 force.print{"auto_research.announce_completed", event.research.localised_name, level}
255 startNextResearch(event.research.force)
260 toggleGui = function(player)
261 if player.gui.top.auto_research_gui then
262 player.gui.top.auto_research_gui.destroy()
264 local force = player.force
265 local config = getConfig(force)
266 local frame = player.gui.top.add{
268 name = "auto_research_gui",
269 direction = "vertical",
270 caption = {"auto_research_gui.title"}
272 local frameflow = frame.add{
274 style = "auto_research_list_flow",
276 direction = "vertical"
280 frameflow.add{type = "checkbox", name = "auto_research_enabled", caption = {"auto_research_gui.enabled"}, tooltip = {"auto_research_gui.enabled_tooltip"}, state = config.enabled or false}
281 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}
282 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}
283 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}
284 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}
289 style = "auto_research_header_label",
290 caption = {"auto_research_gui.research_strategy"}
292 local research_strategies_outer = frameflow.add{
294 style = "auto_research_tech_flow",
295 name = "research_strategies_outer",
296 direction = "horizontal"
298 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"}
299 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"}
300 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"}
301 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"}
302 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"}
303 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"}
305 research_strategies_outer.style.horizontal_spacing = 6
307 -- allowed ingredients
310 style = "auto_research_header_label",
311 caption = {"auto_research_gui.allowed_ingredients_label"}
313 local allowed_ingredients = frameflow.add{
315 style = "auto_research_list_flow",
316 name = "allowed_ingredients",
317 direction = "vertical"
319 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
324 style = "auto_research_header_label",
325 caption = {"auto_research_gui.prioritized_label"}
327 local prioritized = frameflow.add{
328 type = "scroll-pane",
329 name = "prioritized",
330 horizontal_scroll_policy = "never",
331 vertical_scroll_policy = "auto"
333 prioritized.style.top_padding = 5
334 prioritized.style.bottom_padding = 5
335 prioritized.style.maximal_height = 127
336 prioritized.style.minimal_width = 440
337 -- draw prioritized tech list
338 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
340 -- deprioritized techs
343 style = "auto_research_header_label",
344 caption = {"auto_research_gui.deprioritized_label"}
346 local deprioritized = frameflow.add{
347 type = "scroll-pane",
348 name = "deprioritized",
349 horizontal_scroll_policy = "never",
350 vertical_scroll_policy = "auto"
352 deprioritized.style.top_padding = 5
353 deprioritized.style.bottom_padding = 5
354 deprioritized.style.maximal_height = 127
355 deprioritized.style.minimal_width = 440
357 -- draw deprioritized tech list
358 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
361 local searchflow = frameflow.add{
364 style = "auto_research_tech_flow",
365 direction = "horizontal"
369 style = "auto_research_header_label",
370 caption = {"auto_research_gui.search_label"}
374 name = "auto_research_search_text",
375 tooltip = {"auto_research_gui.search_tooltip"}
379 name = "auto_research_ingredients_filter_search_results",
380 caption = {"auto_research_gui.ingredients_filter_search_results"},
381 tooltip = {"auto_research_gui.ingredients_filter_search_results_tooltip"},
382 state = config.filter_search_results or false
384 searchflow.style.horizontal_spacing = 6
385 searchflow.style.vertical_align = "center"
387 local search = frameflow.add{
388 type = "scroll-pane",
390 horizontal_scroll_policy = "never",
391 vertical_scroll_policy = "auto"
393 search.style.top_padding = 5
394 search.style.bottom_padding = 5
395 search.style.maximal_height = 127
396 search.style.minimal_width = 440
398 -- draw search result list
399 gui.updateSearchResult(player, "")
403 onCheckboxClick = function(event)
404 local player = game.players[event.player_index]
405 local force = player.force
406 local name = event.element.name
407 if name == "auto_research_enabled" then
408 setAutoResearch(force, event.element.state)
409 elseif name == "auto_research_queued_only" then
410 setQueuedOnly(force, event.element.state)
411 elseif name == "auto_research_allow_switching" then
412 setAllowSwitching(force, event.element.state)
413 elseif name == "auto_research_announce_completed" then
414 setAnnounceCompletedResearch(force, event.element.state)
415 elseif name == "auto_research_deprioritize_infinite_tech" then
416 setDeprioritizeInfiniteTech(force, event.element.state)
417 elseif name == "auto_research_ingredients_filter_search_results" then
418 local config = getConfig(force)
419 config.filter_search_results = event.element.state
420 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
424 onClick = function(event)
425 local player = game.players[event.player_index]
426 local force = player.force
427 local config = getConfig(force)
428 local name = event.element.name
429 if name == "auto_research_search_text" then
430 if event.button == defines.mouse_button_type.right then
431 player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text = ""
432 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
434 elseif string.find(name, "auto_research_research") then
435 config.research_strategy = string.match(name, "^auto_research_research_(.*)$")
436 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_fast.state = (config.research_strategy == "fast")
437 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_cheap.state = (config.research_strategy == "cheap")
438 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_balanced.state = (config.research_strategy == "balanced")
439 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_slow.state = (config.research_strategy == "slow")
440 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_expensive.state = (config.research_strategy == "expensive")
441 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_random.state = (config.research_strategy == "random")
442 -- start new research
443 startNextResearch(force)
445 local prefix, name = string.match(name, "^auto_research_([^-]*)-(.*)$")
446 if prefix == "allow_ingredient" then
447 config.allowed_ingredients[name] = not config.allowed_ingredients[name]
448 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
449 if player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state then
450 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
452 startNextResearch(force)
453 elseif name and force.technologies[name] then
454 -- remove tech from prioritized list
455 for i = #config.prioritized_techs, 1, -1 do
456 if config.prioritized_techs[i] == name then
457 table.remove(config.prioritized_techs, i)
460 -- and from deprioritized list
461 for i = #config.deprioritized_techs, 1, -1 do
462 if config.deprioritized_techs[i] == name then
463 table.remove(config.deprioritized_techs, i)
466 if prefix == "queue_top" then
467 -- add tech to top of prioritized list
468 table.insert(config.prioritized_techs, 1, name)
469 elseif prefix == "queue_bottom" then
470 -- add tech to bottom of prioritized list
471 table.insert(config.prioritized_techs, name)
472 elseif prefix == "blacklist" then
473 -- add tech to list of deprioritized techs
474 table.insert(config.deprioritized_techs, name)
476 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
477 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
478 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
480 -- start new research
481 startNextResearch(force)
486 updateAllowedIngredientsList = function(flow, player, config)
488 while flow["flow" .. counter] do
489 flow["flow" .. counter].destroy()
490 counter = counter + 1
493 for ingredientname, allowed in pairs(config.allowed_ingredients) do
494 local flowname = "flow" .. math.floor(counter / 10) + 1
495 local ingredientflow = flow[flowname]
496 if not ingredientflow then
497 ingredientflow = flow.add {
499 style = "auto_research_tech_flow",
501 direction = "horizontal"
504 local sprite = "auto_research_tool_" .. ingredientname
505 if not player.gui.is_valid_sprite_path(sprite) then
506 sprite = "auto_research_unknown"
508 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}
509 counter = counter + 1
513 updateTechnologyList = function(scrollpane, technologies, player, show_queue_buttons)
514 if scrollpane.flow then
515 scrollpane.flow.destroy()
517 local flow = scrollpane.add{
519 style = "auto_research_list_flow",
521 direction = "vertical"
523 if #technologies > 0 then
524 for _, techname in pairs(technologies) do
525 local tech = player.force.technologies[techname]
527 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
528 if show_queue_buttons then
529 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. techname, sprite = "auto_research_prioritize_top"}
530 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. techname, sprite = "auto_research_prioritize_bottom"}
532 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_delete-" .. techname, sprite = "auto_research_delete"}
533 entryflow.add{type = "label", style = "auto_research_tech_label", caption = tech.localised_name}
534 for _, ingredient in pairs(tech.research_unit_ingredients) do
535 local sprite = "auto_research_tool_" .. ingredient.name
536 if not player.gui.is_valid_sprite_path(sprite) then
537 sprite = "auto_research_unknown"
539 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
544 local entryflow = flow.add{type = "flow", direction = "horizontal"}
545 entryflow.add{type = "label", caption = {"auto_research_gui.none"}}
549 updateSearchResult = function(player, text)
550 local scrollpane = player.gui.top.auto_research_gui.flow.search
551 if scrollpane.flow then
552 scrollpane.flow.destroy()
554 local flow = scrollpane.add{
556 style = "auto_research_list_flow",
558 direction = "vertical"
560 local ingredients_filter = player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state
561 local config = getConfig(player.force)
563 text = string.lower(text)
564 -- NOTICE: localised name matching does not work at present, pending unlikely changes to Factorio API
565 for name, tech in pairs(player.force.technologies) do
566 if not tech.researched and tech.enabled then
567 local showtech = false
568 if string.find(string.lower(name), text, 1, true) then
569 -- show techs that match by name
571 -- elseif string.find(string.lower(game.technology_prototypes[name].localised_name), text, 1, true) then
572 -- -- show techs that match by localised name
575 for _, effect in pairs(tech.effects) do
576 if string.find(effect.type, text, 1, true) then
577 -- show techs that match by effect type
579 elseif effect.type == "unlock-recipe" then
580 if string.find(effect.recipe, text, 1, true) then
581 -- show techs that match by unlocked recipe name
583 -- elseif string.find(string.lower(game.recipe_prototypes[effect.recipe].localised_name), text, 1, true) then
584 -- -- show techs that match by unlocked recipe localised name
587 for _, product in pairs(game.recipe_prototypes[effect.recipe].products) do
588 if string.find(product.name, text, 1, true) then
589 -- show techs that match by unlocked recipe product name
591 -- elseif string.find(string.lower(game.item_prototypes[product.name].localised_name), text, 1, true) then
592 -- -- show techs that match by unlocked recipe product localised name
595 local prototype = game.item_prototypes[product.name]
597 if prototype.place_result then
598 if string.find(prototype.place_result.name, text, 1, true) then
599 -- show techs that match by unlocked recipe product placed entity name
601 -- elseif string.find(string.lower(game.entity_prototypes[prototype.place_result.name].localised_name), text, 1, true) then
602 -- -- show techs that match by unlocked recipe product placed entity localised name
605 elseif prototype.place_as_equipment_result then
606 if string.find(prototype.place_as_equipment_result.name, text, 1, true) then
607 -- show techs that match by unlocked recipe product placed equipment name
609 -- elseif string.find(string.lower(game.equipment_prototypes[prototype.place_as_equipment_result.name].localised_name), text, 1, true) then
610 -- -- show techs that match by unlocked recipe product placed equipment localised name
613 elseif prototype.place_as_tile_result then
614 if string.find(prototype.place_as_tile_result.result.name, text, 1, true) then
615 -- show techs that match by unlocked recipe product placed tile name
617 -- elseif string.find(string.lower(prototype.place_as_tile_result.result.localised_name), text, 1, true) then
618 -- -- show techs that match by unlocked recipe product placed tile localised name
629 if showtech and config.prioritized_techs then
630 for _, queued_tech in pairs(config.prioritized_techs) do
631 if name == queued_tech then
637 if showtech and config.deprioritized_techs then
638 for _, blacklisted_tech in pairs(config.deprioritized_techs) do
639 if name == blacklisted_tech then
645 if showtech and ingredients_filter then
646 for _, ingredient in pairs(tech.research_unit_ingredients) do
647 if not config.allowed_ingredients[ingredient.name] then
648 -- filter out techs that require disallowed ingredients (optional)
655 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
656 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. name, sprite = "auto_research_prioritize_top"}
657 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. name, sprite = "auto_research_prioritize_bottom"}
658 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_blacklist-" .. name, sprite = "auto_research_deprioritize"}
659 entryflow.add{type = "label", style = "auto_research_tech_label", name = name, caption = tech.localised_name}
660 for _, ingredient in pairs(tech.research_unit_ingredients) do
661 local sprite = "auto_research_tool_" .. ingredient.name
662 if not player.gui.is_valid_sprite_path(sprite) then
663 sprite = "auto_research_unknown"
665 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
674 script.on_configuration_changed(function()
675 for _, force in pairs(game.forces) do
676 getConfig(force, true) -- triggers initialization of force config
679 script.on_event(defines.events.on_player_created, function(event)
680 local force = game.players[event.player_index].force
681 local config = getConfig(force) -- triggers initialization of force config
682 -- set any default queued/blacklisted techs
683 local queued_tech = settings.get_player_settings(game.players[event.player_index])["queued-tech-setting"].value
684 for tech in string.gmatch(queued_tech, "[^,$]+") do
685 tech = string.gsub(tech, "%s+", "")
686 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
687 table.insert(config.prioritized_techs, tech)
690 local blacklisted_tech = settings.get_player_settings(game.players[event.player_index])["blacklisted-tech-setting"].value
691 for tech in string.gmatch(blacklisted_tech, "[^,$]+") do
692 tech = string.gsub(tech, "%s+", "")
693 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
694 table.insert(config.deprioritized_techs, tech)
697 startNextResearch(force, true)
699 script.on_event(defines.events.on_force_created, function(event)
700 getConfig(event.force) -- triggers initialization of force config
702 script.on_event(defines.events.on_research_finished, onResearchFinished)
703 script.on_event(defines.events.on_gui_checked_state_changed, gui.onCheckboxClick)
704 script.on_event(defines.events.on_gui_click, gui.onClick)
705 script.on_event(defines.events.on_gui_text_changed, function(event)
706 if event.element.name ~= "auto_research_search_text" then
709 gui.updateSearchResult(game.players[event.player_index], event.element.text)
713 script.on_event("auto_research_toggle", function(event)
714 local player = game.players[event.player_index]
715 gui.toggleGui(player)
718 -- Add remote interfaces for enabling/disabling Auto Research
719 remote.add_interface("auto_research", {
720 enabled = function(forcename, value) setAutoResearch(game.forces[forcename], value) end,
721 queued_only = function(forcename, value) setQueuedOnly(game.forces[forcename], value) end,
722 allow_switching = function(forcename, value) setAllowSwitching(game.forces[forcename], value) end,
723 announce_completed = function(forcename, value) setAnnounceCompletedResearch(game.forces[forcename], value) end,
724 deprioritize_infinite_tech = function(forcename, value) setDeprioritizeInfiniteTech(game.forces[forcename], value) end