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 if #tech.research_unit_ingredients == 0 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 -- keep queue, just put next_research at the start.
222 if next_research then
224 table.insert(rq, next_research)
227 if force.research_queue[i] == nil then break end
229 if not (force.current_research and config.allow_switching) then
230 table.insert(rq, force.research_queue[i].name)
233 table.insert(rq, force.research_queue[i].name)
237 force.research_queue = rq
241 function onResearchFinished(event)
242 local force = event.research.force
243 local config = getConfig(force)
244 -- remove researched stuff from prioritized_techs and deprioritized_techs
245 for i = #config.prioritized_techs, 1, -1 do
246 local tech = force.technologies[config.prioritized_techs[i]]
247 if not tech or tech.researched then
248 table.remove(config.prioritized_techs, i)
251 for i = #config.deprioritized_techs, 1, -1 do
252 local tech = force.technologies[config.deprioritized_techs[i]]
253 if not tech or tech.researched then
254 table.remove(config.deprioritized_techs, i)
257 -- announce completed research
258 if config.announce_completed and config.no_announce_this_tick ~= game.tick then
259 if config.last_research_finish_tick == game.tick then
260 config.no_announce_this_tick = game.tick
263 if event.research.research_unit_count_formula then
264 level = (event.research.researched and event.research.level) or (event.research.level - 1)
266 force.print{"auto_research.announce_completed", event.research.localised_name, level}
270 startNextResearch(event.research.force)
275 toggleGui = function(player)
276 if player.gui.top.auto_research_gui then
277 player.gui.top.auto_research_gui.destroy()
279 local force = player.force
280 local config = getConfig(force)
281 local frame = player.gui.top.add{
283 name = "auto_research_gui",
284 direction = "vertical",
285 caption = {"auto_research_gui.title"}
287 local frameflow = frame.add{
289 style = "auto_research_list_flow",
291 direction = "vertical"
295 frameflow.add{type = "checkbox", name = "auto_research_enabled", caption = {"auto_research_gui.enabled"}, tooltip = {"auto_research_gui.enabled_tooltip"}, state = config.enabled or false}
296 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}
297 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}
298 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}
299 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}
304 style = "auto_research_header_label",
305 caption = {"auto_research_gui.research_strategy"}
307 local research_strategies_outer = frameflow.add{
309 style = "auto_research_tech_flow",
310 name = "research_strategies_outer",
311 direction = "horizontal"
313 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"}
314 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"}
315 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"}
316 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"}
317 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"}
318 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"}
320 research_strategies_outer.style.horizontal_spacing = 6
322 -- allowed ingredients
325 style = "auto_research_header_label",
326 caption = {"auto_research_gui.allowed_ingredients_label"}
328 local allowed_ingredients = frameflow.add{
330 style = "auto_research_list_flow",
331 name = "allowed_ingredients",
332 direction = "vertical"
334 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
339 style = "auto_research_header_label",
340 caption = {"auto_research_gui.prioritized_label"}
342 local prioritized = frameflow.add{
343 type = "scroll-pane",
344 name = "prioritized",
345 horizontal_scroll_policy = "never",
346 vertical_scroll_policy = "auto"
348 prioritized.style.top_padding = 5
349 prioritized.style.bottom_padding = 5
350 prioritized.style.maximal_height = 127
351 prioritized.style.minimal_width = 440
352 -- draw prioritized tech list
353 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
355 -- deprioritized techs
358 style = "auto_research_header_label",
359 caption = {"auto_research_gui.deprioritized_label"}
361 local deprioritized = frameflow.add{
362 type = "scroll-pane",
363 name = "deprioritized",
364 horizontal_scroll_policy = "never",
365 vertical_scroll_policy = "auto"
367 deprioritized.style.top_padding = 5
368 deprioritized.style.bottom_padding = 5
369 deprioritized.style.maximal_height = 127
370 deprioritized.style.minimal_width = 440
372 -- draw deprioritized tech list
373 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
376 local searchflow = frameflow.add{
379 style = "auto_research_tech_flow",
380 direction = "horizontal"
384 style = "auto_research_header_label",
385 caption = {"auto_research_gui.search_label"}
389 name = "auto_research_search_text",
390 tooltip = {"auto_research_gui.search_tooltip"}
394 name = "auto_research_ingredients_filter_search_results",
395 caption = {"auto_research_gui.ingredients_filter_search_results"},
396 tooltip = {"auto_research_gui.ingredients_filter_search_results_tooltip"},
397 state = config.filter_search_results or false
399 searchflow.style.horizontal_spacing = 6
400 searchflow.style.vertical_align = "center"
402 local search = frameflow.add{
403 type = "scroll-pane",
405 horizontal_scroll_policy = "never",
406 vertical_scroll_policy = "auto"
408 search.style.top_padding = 5
409 search.style.bottom_padding = 5
410 search.style.maximal_height = 127
411 search.style.minimal_width = 440
413 -- draw search result list
414 gui.updateSearchResult(player, "")
418 onCheckboxClick = function(event)
419 local player = game.players[event.player_index]
420 local force = player.force
421 local name = event.element.name
422 if name == "auto_research_enabled" then
423 setAutoResearch(force, event.element.state)
424 elseif name == "auto_research_queued_only" then
425 setQueuedOnly(force, event.element.state)
426 elseif name == "auto_research_allow_switching" then
427 setAllowSwitching(force, event.element.state)
428 elseif name == "auto_research_announce_completed" then
429 setAnnounceCompletedResearch(force, event.element.state)
430 elseif name == "auto_research_deprioritize_infinite_tech" then
431 setDeprioritizeInfiniteTech(force, event.element.state)
432 elseif name == "auto_research_ingredients_filter_search_results" then
433 local config = getConfig(force)
434 config.filter_search_results = event.element.state
435 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
439 onClick = function(event)
440 local player = game.players[event.player_index]
441 local force = player.force
442 local config = getConfig(force)
443 local name = event.element.name
444 if name == "auto_research_search_text" then
445 if event.button == defines.mouse_button_type.right then
446 player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text = ""
447 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
449 elseif string.find(name, "auto_research_research") then
450 config.research_strategy = string.match(name, "^auto_research_research_(.*)$")
451 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_fast.state = (config.research_strategy == "fast")
452 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_cheap.state = (config.research_strategy == "cheap")
453 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_balanced.state = (config.research_strategy == "balanced")
454 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_slow.state = (config.research_strategy == "slow")
455 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_expensive.state = (config.research_strategy == "expensive")
456 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_random.state = (config.research_strategy == "random")
457 -- start new research
458 startNextResearch(force)
460 local prefix, name = string.match(name, "^auto_research_([^-]*)-(.*)$")
461 if prefix == "allow_ingredient" then
462 config.allowed_ingredients[name] = not config.allowed_ingredients[name]
463 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
464 if player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state then
465 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
467 startNextResearch(force)
468 elseif name and force.technologies[name] then
469 -- remove tech from prioritized list
470 for i = #config.prioritized_techs, 1, -1 do
471 if config.prioritized_techs[i] == name then
472 table.remove(config.prioritized_techs, i)
475 -- and from deprioritized list
476 for i = #config.deprioritized_techs, 1, -1 do
477 if config.deprioritized_techs[i] == name then
478 table.remove(config.deprioritized_techs, i)
481 if prefix == "queue_top" then
482 -- add tech to top of prioritized list
483 table.insert(config.prioritized_techs, 1, name)
484 elseif prefix == "queue_bottom" then
485 -- add tech to bottom of prioritized list
486 table.insert(config.prioritized_techs, name)
487 elseif prefix == "blacklist" then
488 -- add tech to list of deprioritized techs
489 table.insert(config.deprioritized_techs, name)
491 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
492 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
493 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
495 -- start new research
496 startNextResearch(force)
501 updateAllowedIngredientsList = function(flow, player, config)
503 while flow["flow" .. counter] do
504 flow["flow" .. counter].destroy()
505 counter = counter + 1
508 for ingredientname, allowed in pairs(config.allowed_ingredients) do
509 local flowname = "flow" .. math.floor(counter / 10) + 1
510 local ingredientflow = flow[flowname]
511 if not ingredientflow then
512 ingredientflow = flow.add {
514 style = "auto_research_tech_flow",
516 direction = "horizontal"
519 local sprite = "auto_research_tool_" .. ingredientname
520 if not helpers.is_valid_sprite_path(sprite) then
521 sprite = "auto_research_unknown"
523 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}
524 counter = counter + 1
528 updateTechnologyList = function(scrollpane, technologies, player, show_queue_buttons)
529 if scrollpane.flow then
530 scrollpane.flow.destroy()
532 local flow = scrollpane.add{
534 style = "auto_research_list_flow",
536 direction = "vertical"
538 if #technologies > 0 then
539 for _, techname in pairs(technologies) do
540 local tech = player.force.technologies[techname]
542 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
543 if show_queue_buttons then
544 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. techname, sprite = "auto_research_prioritize_top"}
545 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. techname, sprite = "auto_research_prioritize_bottom"}
547 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_delete-" .. techname, sprite = "auto_research_delete"}
548 entryflow.add{type = "label", style = "auto_research_tech_label", caption = tech.localised_name}
549 for _, ingredient in pairs(tech.research_unit_ingredients) do
550 local sprite = "auto_research_tool_" .. ingredient.name
551 if not helpers.is_valid_sprite_path(sprite) then
552 sprite = "auto_research_unknown"
554 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
559 local entryflow = flow.add{type = "flow", direction = "horizontal"}
560 entryflow.add{type = "label", caption = {"auto_research_gui.none"}}
564 updateSearchResult = function(player, text)
565 local scrollpane = player.gui.top.auto_research_gui.flow.search
566 if scrollpane.flow then
567 scrollpane.flow.destroy()
569 local flow = scrollpane.add{
571 style = "auto_research_list_flow",
573 direction = "vertical"
575 local ingredients_filter = player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state
576 local config = getConfig(player.force)
578 text = string.lower(text)
579 -- NOTICE: localised name matching does not work at present, pending unlikely changes to Factorio API
580 for name, tech in pairs(player.force.technologies) do
581 if not tech.researched and tech.enabled then
582 local showtech = false
583 if string.find(string.lower(name), text, 1, true) then
584 -- show techs that match by name
586 -- elseif string.find(string.lower(game.technology_prototypes[name].localised_name), text, 1, true) then
587 -- -- show techs that match by localised name
590 for _, effect in pairs(tech.prototype.effects) do
591 if string.find(effect.type, text, 1, true) then
592 -- show techs that match by effect type
594 elseif effect.type == "unlock-recipe" then
595 if string.find(effect.recipe, text, 1, true) then
596 -- show techs that match by unlocked recipe name
598 -- elseif string.find(string.lower(game.recipe_prototypes[effect.recipe].localised_name), text, 1, true) then
599 -- -- show techs that match by unlocked recipe localised name
602 for _, product in pairs(prototypes.recipe[effect.recipe].products) do
603 if string.find(product.name, text, 1, true) then
604 -- show techs that match by unlocked recipe product name
606 -- elseif string.find(string.lower(game.item_prototypes[product.name].localised_name), text, 1, true) then
607 -- -- show techs that match by unlocked recipe product localised name
610 local prototype = prototypes.item[product.name]
612 if prototype.place_result then
613 if string.find(prototype.place_result.name, text, 1, true) then
614 -- show techs that match by unlocked recipe product placed entity name
616 -- elseif string.find(string.lower(game.entity_prototypes[prototype.place_result.name].localised_name), text, 1, true) then
617 -- -- show techs that match by unlocked recipe product placed entity localised name
620 elseif prototype.place_as_equipment_result then
621 if string.find(prototype.place_as_equipment_result.name, text, 1, true) then
622 -- show techs that match by unlocked recipe product placed equipment name
624 -- elseif string.find(string.lower(game.equipment_prototypes[prototype.place_as_equipment_result.name].localised_name), text, 1, true) then
625 -- -- show techs that match by unlocked recipe product placed equipment localised name
628 elseif prototype.place_as_tile_result then
629 if string.find(prototype.place_as_tile_result.result.name, text, 1, true) then
630 -- show techs that match by unlocked recipe product placed tile name
632 -- elseif string.find(string.lower(prototype.place_as_tile_result.result.localised_name), text, 1, true) then
633 -- -- show techs that match by unlocked recipe product placed tile localised name
644 if showtech and config.prioritized_techs then
645 for _, queued_tech in pairs(config.prioritized_techs) do
646 if name == queued_tech then
652 if showtech and config.deprioritized_techs then
653 for _, blacklisted_tech in pairs(config.deprioritized_techs) do
654 if name == blacklisted_tech then
660 if showtech and ingredients_filter then
661 for _, ingredient in pairs(tech.research_unit_ingredients) do
662 if not config.allowed_ingredients[ingredient.name] then
663 -- filter out techs that require disallowed ingredients (optional)
670 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
671 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. name, sprite = "auto_research_prioritize_top"}
672 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. name, sprite = "auto_research_prioritize_bottom"}
673 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_blacklist-" .. name, sprite = "auto_research_deprioritize"}
674 entryflow.add{type = "label", style = "auto_research_tech_label", name = name, caption = tech.localised_name}
675 for _, ingredient in pairs(tech.research_unit_ingredients) do
676 local sprite = "auto_research_tool_" .. ingredient.name
677 if not helpers.is_valid_sprite_path(sprite) then
678 sprite = "auto_research_unknown"
680 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
689 script.on_configuration_changed(function()
690 for _, force in pairs(game.forces) do
691 getConfig(force, true) -- triggers initialization of force config
694 script.on_event(defines.events.on_player_created, function(event)
695 local force = game.players[event.player_index].force
696 local config = getConfig(force) -- triggers initialization of force config
697 -- set any default queued/blacklisted techs
698 local queued_tech = settings.get_player_settings(game.players[event.player_index])["queued-tech-setting"].value
699 for tech in string.gmatch(queued_tech, "[^,$]+") do
700 tech = string.gsub(tech, "%s+", "")
701 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
702 table.insert(config.prioritized_techs, tech)
705 local blacklisted_tech = settings.get_player_settings(game.players[event.player_index])["blacklisted-tech-setting"].value
706 for tech in string.gmatch(blacklisted_tech, "[^,$]+") do
707 tech = string.gsub(tech, "%s+", "")
708 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
709 table.insert(config.deprioritized_techs, tech)
712 startNextResearch(force, true)
714 script.on_event(defines.events.on_force_created, function(event)
715 getConfig(event.force) -- triggers initialization of force config
717 script.on_event(defines.events.on_research_finished, onResearchFinished)
718 script.on_event(defines.events.on_gui_checked_state_changed, gui.onCheckboxClick)
719 script.on_event(defines.events.on_gui_click, gui.onClick)
720 script.on_event(defines.events.on_gui_text_changed, function(event)
721 if event.element.name ~= "auto_research_search_text" then
724 gui.updateSearchResult(game.players[event.player_index], event.element.text)
728 script.on_event("auto_research_toggle", function(event)
729 local player = game.players[event.player_index]
730 gui.toggleGui(player)
733 -- Add remote interfaces for enabling/disabling Auto Research
734 remote.add_interface("auto_research", {
735 enabled = function(forcename, value) setAutoResearch(game.forces[forcename], value) end,
736 queued_only = function(forcename, value) setQueuedOnly(game.forces[forcename], value) end,
737 allow_switching = function(forcename, value) setAllowSwitching(game.forces[forcename], value) end,
738 announce_completed = function(forcename, value) setAnnounceCompletedResearch(game.forces[forcename], value) end,
739 deprioritize_infinite_tech = function(forcename, value) setDeprioritizeInfiniteTech(game.forces[forcename], value) end