1 local auto_research_config = {}
3 function getConfig(force, config_changed)
4 -- Disable Research Queue popup
5 if remote.interfaces.RQ and remote.interfaces.RQ["popup"] then
6 remote.call("RQ", "popup", false)
9 if not auto_research_config[force.name] then
10 auto_research_config[force.name] = {
11 prioritized_techs = {}, -- "prioritized" is "queued". kept for backwards compatability (because i'm lazy and don't want migration code)
12 deprioritized_techs = {} -- "deprioritized" is "blacklisted". kept for backwards compatability (because i'm lazy and don't want migration code)
14 -- Enable Auto Research
15 setAutoResearch(force, true)
17 -- Disable queued only
18 setQueuedOnly(force, false)
20 -- Allow switching research
21 setAllowSwitching(force, true)
23 -- Print researched technology
24 setAnnounceCompletedResearch(force, true)
27 -- set research strategy
28 auto_research_config[force.name].research_strategy = auto_research_config[force.name].research_strategy or "balanced"
30 if config_changed or not auto_research_config[force.name].allowed_ingredients or not auto_research_config[force.name].infinite_research then
31 -- remember any old ingredients
32 local old_ingredients = {}
33 if auto_research_config[force.name].allowed_ingredients then
34 for name, enabled in pairs(auto_research_config[force.name].allowed_ingredients) do
35 old_ingredients[name] = enabled
38 -- find all possible tech ingredients
39 -- also scan for research that are infinite: techs that have no successor and tech.research_unit_count_formula is not nil
40 auto_research_config[force.name].allowed_ingredients = {}
41 auto_research_config[force.name].infinite_research = {}
42 local finite_research = {}
43 for _, tech in pairs(force.technologies) do
44 for _, ingredient in pairs(tech.research_unit_ingredients) do
45 auto_research_config[force.name].allowed_ingredients[ingredient.name] = (old_ingredients[ingredient.name] == nil or old_ingredients[ingredient.name])
47 if tech.research_unit_count_formula then
48 auto_research_config[force.name].infinite_research[tech.name] = tech
50 for _, pretech in pairs(tech.prerequisites) do
51 if pretech.enabled and not pretech.researched then
52 finite_research[pretech.name] = true
56 for techname, _ in pairs(finite_research) do
57 auto_research_config[force.name].infinite_research[techname] = nil
61 return auto_research_config[force.name]
64 function setAutoResearch(force, enabled)
68 local config = getConfig(force)
69 config.enabled = enabled
73 startNextResearch(force)
77 function setQueuedOnly(force, enabled)
81 getConfig(force).prioritized_only = enabled
84 startNextResearch(force)
87 function setAllowSwitching(force, enabled)
91 getConfig(force).allow_switching = enabled
94 startNextResearch(force)
97 function setAnnounceCompletedResearch(force, enabled)
101 getConfig(force).announce_completed = enabled
104 function setDeprioritizeInfiniteTech(force, enabled)
108 getConfig(force).deprioritize_infinite_tech = enabled
110 -- start new research
111 startNextResearch(force)
114 function getPretechs(tech)
116 pretechs[#pretechs + 1] = tech
118 while (index <= #pretechs) do
119 for _, pretech in pairs(pretechs[index].prerequisites) do
120 if pretech.enabled and not pretech.researched then
121 pretechs[#pretechs + 1] = pretech
129 function canResearch(force, tech, config)
130 if not tech or tech.researched or not tech.enabled or tech.prototype.hidden then
133 for _, pretech in pairs(tech.prerequisites) do
134 if not pretech.researched then
138 for _, ingredient in pairs(tech.research_unit_ingredients) do
139 if not config.allowed_ingredients[ingredient.name] then
143 for _, deprioritized in pairs(config.deprioritized_techs) do
144 if tech.name == deprioritized then
151 function startNextResearch(force, override_spam_detection)
152 local config = getConfig(force)
153 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
156 config.last_research_finish_tick = game.tick -- if multiple research finish same tick for same force, the user probably enabled all techs
158 -- function for calculating tech effort
159 local calcEffort = function(tech)
160 local ingredientCount = function(ingredients)
161 local tech_ingredients = 0
162 for _, ingredient in pairs(tech.research_unit_ingredients) do
163 tech_ingredients = tech_ingredients + ingredient.amount
165 return tech_ingredients
168 if config.research_strategy == "fast" then
169 effort = math.max(tech.research_unit_energy, 1) * math.max(tech.research_unit_count, 1)
170 elseif config.research_strategy == "slow" then
171 effort = math.max(tech.research_unit_energy, 1) * math.max(tech.research_unit_count, 1) * -1
172 elseif config.research_strategy == "cheap" then
173 effort = math.max(ingredientCount(tech.research_unit_ingredients), 1) * math.max(tech.research_unit_count, 1)
174 elseif config.research_strategy == "expensive" then
175 effort = math.max(ingredientCount(tech.research_unit_ingredients), 1) * math.max(tech.research_unit_count, 1) * -1
176 elseif config.research_strategy == "balanced" then
177 effort = math.max(tech.research_unit_count, 1) * math.max(tech.research_unit_energy, 1) * math.max(ingredientCount(tech.research_unit_ingredients), 1)
179 effort = math.random(1, 999)
181 if (config.deprioritize_infinite_tech and config.infinite_research[tech.name]) then
182 return effort * (effort > 0 and 1000 or -1000)
188 -- see if there are some techs we should research first
189 local next_research = nil
190 local least_effort = nil
191 for _, techname in pairs(config.prioritized_techs) do
192 local tech = force.technologies[techname]
193 if tech and not next_research then
194 local pretechs = getPretechs(tech)
195 for _, pretech in pairs(pretechs) do
196 local effort = calcEffort(pretech)
197 if (not least_effort or effort < least_effort) and canResearch(force, pretech, config) then
198 next_research = pretech.name
199 least_effort = effort
205 -- if no queued tech should be researched then research the "least effort" tech not researched yet
206 if not config.prioritized_only and not next_research then
207 for techname, tech in pairs(force.technologies) do
208 if tech.enabled and not tech.researched then
209 local effort = calcEffort(tech)
210 if (not least_effort or effort < least_effort) and canResearch(force, tech, config) then
211 next_research = techname
212 least_effort = effort
218 if next_research then
220 table.insert(rq, next_research)
223 if force.research_queue[i] == nil then break end
224 table.insert(rq, force.research_queue[i])
227 force.research_queue = rq
231 function onResearchFinished(event)
232 local force = event.research.force
233 local config = getConfig(force)
234 -- remove researched stuff from prioritized_techs and deprioritized_techs
235 for i = #config.prioritized_techs, 1, -1 do
236 local tech = force.technologies[config.prioritized_techs[i]]
237 if not tech or tech.researched then
238 table.remove(config.prioritized_techs, i)
241 for i = #config.deprioritized_techs, 1, -1 do
242 local tech = force.technologies[config.deprioritized_techs[i]]
243 if not tech or tech.researched then
244 table.remove(config.deprioritized_techs, i)
247 -- announce completed research
248 if config.announce_completed and config.no_announce_this_tick ~= game.tick then
249 if config.last_research_finish_tick == game.tick then
250 config.no_announce_this_tick = game.tick
253 if event.research.research_unit_count_formula then
254 level = (event.research.researched and event.research.level) or (event.research.level - 1)
256 force.print{"auto_research.announce_completed", event.research.localised_name, level}
260 startNextResearch(event.research.force)
265 toggleGui = function(player)
266 if player.gui.top.auto_research_gui then
267 player.gui.top.auto_research_gui.destroy()
269 local force = player.force
270 local config = getConfig(force)
271 local frame = player.gui.top.add{
273 name = "auto_research_gui",
274 direction = "vertical",
275 caption = {"auto_research_gui.title"}
277 local frameflow = frame.add{
279 style = "auto_research_list_flow",
281 direction = "vertical"
285 frameflow.add{type = "checkbox", name = "auto_research_enabled", caption = {"auto_research_gui.enabled"}, tooltip = {"auto_research_gui.enabled_tooltip"}, state = config.enabled or false}
286 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}
287 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}
288 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}
289 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}
294 style = "auto_research_header_label",
295 caption = {"auto_research_gui.research_strategy"}
297 local research_strategies_outer = frameflow.add{
299 style = "auto_research_tech_flow",
300 name = "research_strategies_outer",
301 direction = "horizontal"
303 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"}
304 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"}
305 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"}
306 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"}
307 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"}
308 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"}
310 research_strategies_outer.style.horizontal_spacing = 6
312 -- allowed ingredients
315 style = "auto_research_header_label",
316 caption = {"auto_research_gui.allowed_ingredients_label"}
318 local allowed_ingredients = frameflow.add{
320 style = "auto_research_list_flow",
321 name = "allowed_ingredients",
322 direction = "vertical"
324 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
329 style = "auto_research_header_label",
330 caption = {"auto_research_gui.prioritized_label"}
332 local prioritized = frameflow.add{
333 type = "scroll-pane",
334 name = "prioritized",
335 horizontal_scroll_policy = "never",
336 vertical_scroll_policy = "auto"
338 prioritized.style.top_padding = 5
339 prioritized.style.bottom_padding = 5
340 prioritized.style.maximal_height = 127
341 prioritized.style.minimal_width = 440
342 -- draw prioritized tech list
343 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
345 -- deprioritized techs
348 style = "auto_research_header_label",
349 caption = {"auto_research_gui.deprioritized_label"}
351 local deprioritized = frameflow.add{
352 type = "scroll-pane",
353 name = "deprioritized",
354 horizontal_scroll_policy = "never",
355 vertical_scroll_policy = "auto"
357 deprioritized.style.top_padding = 5
358 deprioritized.style.bottom_padding = 5
359 deprioritized.style.maximal_height = 127
360 deprioritized.style.minimal_width = 440
362 -- draw deprioritized tech list
363 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
366 local searchflow = frameflow.add{
369 style = "auto_research_tech_flow",
370 direction = "horizontal"
374 style = "auto_research_header_label",
375 caption = {"auto_research_gui.search_label"}
379 name = "auto_research_search_text",
380 tooltip = {"auto_research_gui.search_tooltip"}
384 name = "auto_research_ingredients_filter_search_results",
385 caption = {"auto_research_gui.ingredients_filter_search_results"},
386 tooltip = {"auto_research_gui.ingredients_filter_search_results_tooltip"},
387 state = config.filter_search_results or false
389 searchflow.style.horizontal_spacing = 6
390 searchflow.style.vertical_align = "center"
392 local search = frameflow.add{
393 type = "scroll-pane",
395 horizontal_scroll_policy = "never",
396 vertical_scroll_policy = "auto"
398 search.style.top_padding = 5
399 search.style.bottom_padding = 5
400 search.style.maximal_height = 127
401 search.style.minimal_width = 440
403 -- draw search result list
404 gui.updateSearchResult(player, "")
408 onCheckboxClick = function(event)
409 local player = game.players[event.player_index]
410 local force = player.force
411 local name = event.element.name
412 if name == "auto_research_enabled" then
413 setAutoResearch(force, event.element.state)
414 elseif name == "auto_research_queued_only" then
415 setQueuedOnly(force, event.element.state)
416 elseif name == "auto_research_allow_switching" then
417 setAllowSwitching(force, event.element.state)
418 elseif name == "auto_research_announce_completed" then
419 setAnnounceCompletedResearch(force, event.element.state)
420 elseif name == "auto_research_deprioritize_infinite_tech" then
421 setDeprioritizeInfiniteTech(force, event.element.state)
422 elseif name == "auto_research_ingredients_filter_search_results" then
423 local config = getConfig(force)
424 config.filter_search_results = event.element.state
425 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
429 onClick = function(event)
430 local player = game.players[event.player_index]
431 local force = player.force
432 local config = getConfig(force)
433 local name = event.element.name
434 if name == "auto_research_search_text" then
435 if event.button == defines.mouse_button_type.right then
436 player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text = ""
437 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
439 elseif string.find(name, "auto_research_research") then
440 config.research_strategy = string.match(name, "^auto_research_research_(.*)$")
441 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_fast.state = (config.research_strategy == "fast")
442 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_cheap.state = (config.research_strategy == "cheap")
443 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_balanced.state = (config.research_strategy == "balanced")
444 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_slow.state = (config.research_strategy == "slow")
445 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_expensive.state = (config.research_strategy == "expensive")
446 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_random.state = (config.research_strategy == "random")
447 -- start new research
448 startNextResearch(force)
450 local prefix, name = string.match(name, "^auto_research_([^-]*)-(.*)$")
451 if prefix == "allow_ingredient" then
452 config.allowed_ingredients[name] = not config.allowed_ingredients[name]
453 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
454 if player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state then
455 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
457 startNextResearch(force)
458 elseif name and force.technologies[name] then
459 -- remove tech from prioritized list
460 for i = #config.prioritized_techs, 1, -1 do
461 if config.prioritized_techs[i] == name then
462 table.remove(config.prioritized_techs, i)
465 -- and from deprioritized list
466 for i = #config.deprioritized_techs, 1, -1 do
467 if config.deprioritized_techs[i] == name then
468 table.remove(config.deprioritized_techs, i)
471 if prefix == "queue_top" then
472 -- add tech to top of prioritized list
473 table.insert(config.prioritized_techs, 1, name)
474 elseif prefix == "queue_bottom" then
475 -- add tech to bottom of prioritized list
476 table.insert(config.prioritized_techs, name)
477 elseif prefix == "blacklist" then
478 -- add tech to list of deprioritized techs
479 table.insert(config.deprioritized_techs, name)
481 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
482 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
483 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
485 -- start new research
486 startNextResearch(force)
491 updateAllowedIngredientsList = function(flow, player, config)
493 while flow["flow" .. counter] do
494 flow["flow" .. counter].destroy()
495 counter = counter + 1
498 for ingredientname, allowed in pairs(config.allowed_ingredients) do
499 local flowname = "flow" .. math.floor(counter / 10) + 1
500 local ingredientflow = flow[flowname]
501 if not ingredientflow then
502 ingredientflow = flow.add {
504 style = "auto_research_tech_flow",
506 direction = "horizontal"
509 local sprite = "auto_research_tool_" .. ingredientname
510 if not helpers.is_valid_sprite_path(sprite) then
511 sprite = "auto_research_unknown"
513 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}
514 counter = counter + 1
518 updateTechnologyList = function(scrollpane, technologies, player, show_queue_buttons)
519 if scrollpane.flow then
520 scrollpane.flow.destroy()
522 local flow = scrollpane.add{
524 style = "auto_research_list_flow",
526 direction = "vertical"
528 if #technologies > 0 then
529 for _, techname in pairs(technologies) do
530 local tech = player.force.technologies[techname]
532 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
533 if show_queue_buttons then
534 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. techname, sprite = "auto_research_prioritize_top"}
535 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. techname, sprite = "auto_research_prioritize_bottom"}
537 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_delete-" .. techname, sprite = "auto_research_delete"}
538 entryflow.add{type = "label", style = "auto_research_tech_label", caption = tech.localised_name}
539 for _, ingredient in pairs(tech.research_unit_ingredients) do
540 local sprite = "auto_research_tool_" .. ingredient.name
541 if not helpers.is_valid_sprite_path(sprite) then
542 sprite = "auto_research_unknown"
544 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
549 local entryflow = flow.add{type = "flow", direction = "horizontal"}
550 entryflow.add{type = "label", caption = {"auto_research_gui.none"}}
554 updateSearchResult = function(player, text)
555 local scrollpane = player.gui.top.auto_research_gui.flow.search
556 if scrollpane.flow then
557 scrollpane.flow.destroy()
559 local flow = scrollpane.add{
561 style = "auto_research_list_flow",
563 direction = "vertical"
565 local ingredients_filter = player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state
566 local config = getConfig(player.force)
568 text = string.lower(text)
569 -- NOTICE: localised name matching does not work at present, pending unlikely changes to Factorio API
570 for name, tech in pairs(player.force.technologies) do
571 if not tech.researched and tech.enabled then
572 local showtech = false
573 if string.find(string.lower(name), text, 1, true) then
574 -- show techs that match by name
576 -- elseif string.find(string.lower(game.technology_prototypes[name].localised_name), text, 1, true) then
577 -- -- show techs that match by localised name
580 for _, effect in pairs(tech.effects) do
581 if string.find(effect.type, text, 1, true) then
582 -- show techs that match by effect type
584 elseif effect.type == "unlock-recipe" then
585 if string.find(effect.recipe, text, 1, true) then
586 -- show techs that match by unlocked recipe name
588 -- elseif string.find(string.lower(game.recipe_prototypes[effect.recipe].localised_name), text, 1, true) then
589 -- -- show techs that match by unlocked recipe localised name
592 for _, product in pairs(game.recipe_prototypes[effect.recipe].products) do
593 if string.find(product.name, text, 1, true) then
594 -- show techs that match by unlocked recipe product name
596 -- elseif string.find(string.lower(game.item_prototypes[product.name].localised_name), text, 1, true) then
597 -- -- show techs that match by unlocked recipe product localised name
600 local prototype = game.item_prototypes[product.name]
602 if prototype.place_result then
603 if string.find(prototype.place_result.name, text, 1, true) then
604 -- show techs that match by unlocked recipe product placed entity name
606 -- elseif string.find(string.lower(game.entity_prototypes[prototype.place_result.name].localised_name), text, 1, true) then
607 -- -- show techs that match by unlocked recipe product placed entity localised name
610 elseif prototype.place_as_equipment_result then
611 if string.find(prototype.place_as_equipment_result.name, text, 1, true) then
612 -- show techs that match by unlocked recipe product placed equipment name
614 -- elseif string.find(string.lower(game.equipment_prototypes[prototype.place_as_equipment_result.name].localised_name), text, 1, true) then
615 -- -- show techs that match by unlocked recipe product placed equipment localised name
618 elseif prototype.place_as_tile_result then
619 if string.find(prototype.place_as_tile_result.result.name, text, 1, true) then
620 -- show techs that match by unlocked recipe product placed tile name
622 -- elseif string.find(string.lower(prototype.place_as_tile_result.result.localised_name), text, 1, true) then
623 -- -- show techs that match by unlocked recipe product placed tile localised name
634 if showtech and config.prioritized_techs then
635 for _, queued_tech in pairs(config.prioritized_techs) do
636 if name == queued_tech then
642 if showtech and config.deprioritized_techs then
643 for _, blacklisted_tech in pairs(config.deprioritized_techs) do
644 if name == blacklisted_tech then
650 if showtech and ingredients_filter then
651 for _, ingredient in pairs(tech.research_unit_ingredients) do
652 if not config.allowed_ingredients[ingredient.name] then
653 -- filter out techs that require disallowed ingredients (optional)
660 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
661 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. name, sprite = "auto_research_prioritize_top"}
662 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. name, sprite = "auto_research_prioritize_bottom"}
663 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_blacklist-" .. name, sprite = "auto_research_deprioritize"}
664 entryflow.add{type = "label", style = "auto_research_tech_label", name = name, caption = tech.localised_name}
665 for _, ingredient in pairs(tech.research_unit_ingredients) do
666 local sprite = "auto_research_tool_" .. ingredient.name
667 if not helpers.is_valid_sprite_path(sprite) then
668 sprite = "auto_research_unknown"
670 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
679 script.on_configuration_changed(function()
680 for _, force in pairs(game.forces) do
681 getConfig(force, true) -- triggers initialization of force config
684 script.on_event(defines.events.on_player_created, function(event)
685 local force = game.players[event.player_index].force
686 local config = getConfig(force) -- triggers initialization of force config
687 -- set any default queued/blacklisted techs
688 local queued_tech = settings.get_player_settings(game.players[event.player_index])["queued-tech-setting"].value
689 for tech in string.gmatch(queued_tech, "[^,$]+") do
690 tech = string.gsub(tech, "%s+", "")
691 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
692 table.insert(config.prioritized_techs, tech)
695 local blacklisted_tech = settings.get_player_settings(game.players[event.player_index])["blacklisted-tech-setting"].value
696 for tech in string.gmatch(blacklisted_tech, "[^,$]+") do
697 tech = string.gsub(tech, "%s+", "")
698 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
699 table.insert(config.deprioritized_techs, tech)
702 startNextResearch(force, true)
704 script.on_event(defines.events.on_force_created, function(event)
705 getConfig(event.force) -- triggers initialization of force config
707 script.on_event(defines.events.on_research_finished, onResearchFinished)
708 script.on_event(defines.events.on_gui_checked_state_changed, gui.onCheckboxClick)
709 script.on_event(defines.events.on_gui_click, gui.onClick)
710 script.on_event(defines.events.on_gui_text_changed, function(event)
711 if event.element.name ~= "auto_research_search_text" then
714 gui.updateSearchResult(game.players[event.player_index], event.element.text)
718 script.on_event("auto_research_toggle", function(event)
719 local player = game.players[event.player_index]
720 gui.toggleGui(player)
723 -- Add remote interfaces for enabling/disabling Auto Research
724 remote.add_interface("auto_research", {
725 enabled = function(forcename, value) setAutoResearch(game.forces[forcename], value) end,
726 queued_only = function(forcename, value) setQueuedOnly(game.forces[forcename], value) end,
727 allow_switching = function(forcename, value) setAllowSwitching(game.forces[forcename], value) end,
728 announce_completed = function(forcename, value) setAnnounceCompletedResearch(game.forces[forcename], value) end,
729 deprioritize_infinite_tech = function(forcename, value) setDeprioritizeInfiniteTech(game.forces[forcename], value) end