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
227 table.insert(rq, next_research)
230 if force.research_queue[i] == nil then break end
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)
240 force.research_queue = rq
244 function onResearchFinished(event)
245 local force = event.research.force
246 local config = getConfig(force)
247 -- remove researched stuff from prioritized_techs and deprioritized_techs
248 for i = #config.prioritized_techs, 1, -1 do
249 local tech = force.technologies[config.prioritized_techs[i]]
250 if not tech or tech.researched then
251 table.remove(config.prioritized_techs, i)
254 for i = #config.deprioritized_techs, 1, -1 do
255 local tech = force.technologies[config.deprioritized_techs[i]]
256 if not tech or tech.researched then
257 table.remove(config.deprioritized_techs, i)
260 -- announce completed research
261 if config.announce_completed and config.no_announce_this_tick ~= game.tick then
262 if config.last_research_finish_tick == game.tick then
263 config.no_announce_this_tick = game.tick
266 if event.research.research_unit_count_formula then
267 level = (event.research.researched and event.research.level) or (event.research.level - 1)
269 force.print{"auto_research.announce_completed", event.research.localised_name, level}
273 startNextResearch(event.research.force)
278 toggleGui = function(player)
279 if player.gui.top.auto_research_gui then
280 player.gui.top.auto_research_gui.destroy()
282 local force = player.force
283 local config = getConfig(force)
284 local frame = player.gui.top.add{
286 name = "auto_research_gui",
287 direction = "vertical",
288 caption = {"auto_research_gui.title"}
290 local frameflow = frame.add{
292 style = "auto_research_list_flow",
294 direction = "vertical"
298 frameflow.add{type = "checkbox", name = "auto_research_enabled", caption = {"auto_research_gui.enabled"}, tooltip = {"auto_research_gui.enabled_tooltip"}, state = config.enabled or false}
299 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}
300 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}
301 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}
302 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}
307 style = "auto_research_header_label",
308 caption = {"auto_research_gui.research_strategy"}
310 local research_strategies_outer = frameflow.add{
312 style = "auto_research_tech_flow",
313 name = "research_strategies_outer",
314 direction = "horizontal"
316 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"}
317 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"}
318 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"}
319 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"}
320 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"}
321 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"}
323 research_strategies_outer.style.horizontal_spacing = 6
325 -- allowed ingredients
328 style = "auto_research_header_label",
329 caption = {"auto_research_gui.allowed_ingredients_label"}
331 local allowed_ingredients = frameflow.add{
333 style = "auto_research_list_flow",
334 name = "allowed_ingredients",
335 direction = "vertical"
337 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
342 style = "auto_research_header_label",
343 caption = {"auto_research_gui.prioritized_label"}
345 local prioritized = frameflow.add{
346 type = "scroll-pane",
347 name = "prioritized",
348 horizontal_scroll_policy = "never",
349 vertical_scroll_policy = "auto"
351 prioritized.style.top_padding = 5
352 prioritized.style.bottom_padding = 5
353 prioritized.style.maximal_height = 127
354 prioritized.style.minimal_width = 440
355 -- draw prioritized tech list
356 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
358 -- deprioritized techs
361 style = "auto_research_header_label",
362 caption = {"auto_research_gui.deprioritized_label"}
364 local deprioritized = frameflow.add{
365 type = "scroll-pane",
366 name = "deprioritized",
367 horizontal_scroll_policy = "never",
368 vertical_scroll_policy = "auto"
370 deprioritized.style.top_padding = 5
371 deprioritized.style.bottom_padding = 5
372 deprioritized.style.maximal_height = 127
373 deprioritized.style.minimal_width = 440
375 -- draw deprioritized tech list
376 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
379 local searchflow = frameflow.add{
382 style = "auto_research_tech_flow",
383 direction = "horizontal"
387 style = "auto_research_header_label",
388 caption = {"auto_research_gui.search_label"}
392 name = "auto_research_search_text",
393 tooltip = {"auto_research_gui.search_tooltip"}
397 name = "auto_research_ingredients_filter_search_results",
398 caption = {"auto_research_gui.ingredients_filter_search_results"},
399 tooltip = {"auto_research_gui.ingredients_filter_search_results_tooltip"},
400 state = config.filter_search_results or false
402 searchflow.style.horizontal_spacing = 6
403 searchflow.style.vertical_align = "center"
405 local search = frameflow.add{
406 type = "scroll-pane",
408 horizontal_scroll_policy = "never",
409 vertical_scroll_policy = "auto"
411 search.style.top_padding = 5
412 search.style.bottom_padding = 5
413 search.style.maximal_height = 127
414 search.style.minimal_width = 440
416 -- draw search result list
417 gui.updateSearchResult(player, "")
421 onCheckboxClick = function(event)
422 local player = game.players[event.player_index]
423 local force = player.force
424 local name = event.element.name
425 if name == "auto_research_enabled" then
426 setAutoResearch(force, event.element.state)
427 elseif name == "auto_research_queued_only" then
428 setQueuedOnly(force, event.element.state)
429 elseif name == "auto_research_allow_switching" then
430 setAllowSwitching(force, event.element.state)
431 elseif name == "auto_research_announce_completed" then
432 setAnnounceCompletedResearch(force, event.element.state)
433 elseif name == "auto_research_deprioritize_infinite_tech" then
434 setDeprioritizeInfiniteTech(force, event.element.state)
435 elseif name == "auto_research_ingredients_filter_search_results" then
436 local config = getConfig(force)
437 config.filter_search_results = event.element.state
438 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
442 onClick = function(event)
443 local player = game.players[event.player_index]
444 local force = player.force
445 local config = getConfig(force)
446 local name = event.element.name
447 if name == "auto_research_search_text" then
448 if event.button == defines.mouse_button_type.right then
449 player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text = ""
450 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
452 elseif string.find(name, "auto_research_research") then
453 config.research_strategy = string.match(name, "^auto_research_research_(.*)$")
454 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_fast.state = (config.research_strategy == "fast")
455 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_cheap.state = (config.research_strategy == "cheap")
456 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_balanced.state = (config.research_strategy == "balanced")
457 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_slow.state = (config.research_strategy == "slow")
458 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_expensive.state = (config.research_strategy == "expensive")
459 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_random.state = (config.research_strategy == "random")
460 -- start new research
461 startNextResearch(force)
463 local prefix, name = string.match(name, "^auto_research_([^-]*)-(.*)$")
464 if prefix == "allow_ingredient" then
465 config.allowed_ingredients[name] = not config.allowed_ingredients[name]
466 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
467 if player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state then
468 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
470 startNextResearch(force)
471 elseif name and force.technologies[name] then
472 -- remove tech from prioritized list
473 for i = #config.prioritized_techs, 1, -1 do
474 if config.prioritized_techs[i] == name then
475 table.remove(config.prioritized_techs, i)
478 -- and from deprioritized list
479 for i = #config.deprioritized_techs, 1, -1 do
480 if config.deprioritized_techs[i] == name then
481 table.remove(config.deprioritized_techs, i)
484 if prefix == "queue_top" then
485 -- add tech to top of prioritized list
486 table.insert(config.prioritized_techs, 1, name)
487 elseif prefix == "queue_bottom" then
488 -- add tech to bottom of prioritized list
489 table.insert(config.prioritized_techs, name)
490 elseif prefix == "blacklist" then
491 -- add tech to list of deprioritized techs
492 table.insert(config.deprioritized_techs, name)
494 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
495 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
496 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
498 -- start new research
499 startNextResearch(force)
504 updateAllowedIngredientsList = function(flow, player, config)
506 while flow["flow" .. counter] do
507 flow["flow" .. counter].destroy()
508 counter = counter + 1
511 for ingredientname, allowed in pairs(config.allowed_ingredients) do
512 local flowname = "flow" .. math.floor(counter / 10) + 1
513 local ingredientflow = flow[flowname]
514 if not ingredientflow then
515 ingredientflow = flow.add {
517 style = "auto_research_tech_flow",
519 direction = "horizontal"
522 local sprite = "auto_research_tool_" .. ingredientname
523 if not helpers.is_valid_sprite_path(sprite) then
524 sprite = "auto_research_unknown"
526 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}
527 counter = counter + 1
531 updateTechnologyList = function(scrollpane, technologies, player, show_queue_buttons)
532 if scrollpane.flow then
533 scrollpane.flow.destroy()
535 local flow = scrollpane.add{
537 style = "auto_research_list_flow",
539 direction = "vertical"
541 if #technologies > 0 then
542 for _, techname in pairs(technologies) do
543 local tech = player.force.technologies[techname]
545 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
546 if show_queue_buttons then
547 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. techname, sprite = "auto_research_prioritize_top"}
548 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. techname, sprite = "auto_research_prioritize_bottom"}
550 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_delete-" .. techname, sprite = "auto_research_delete"}
551 entryflow.add{type = "label", style = "auto_research_tech_label", caption = tech.localised_name}
552 for _, ingredient in pairs(tech.research_unit_ingredients) do
553 local sprite = "auto_research_tool_" .. ingredient.name
554 if not helpers.is_valid_sprite_path(sprite) then
555 sprite = "auto_research_unknown"
557 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
562 local entryflow = flow.add{type = "flow", direction = "horizontal"}
563 entryflow.add{type = "label", caption = {"auto_research_gui.none"}}
567 updateSearchResult = function(player, text)
568 local scrollpane = player.gui.top.auto_research_gui.flow.search
569 if scrollpane.flow then
570 scrollpane.flow.destroy()
572 local flow = scrollpane.add{
574 style = "auto_research_list_flow",
576 direction = "vertical"
578 local ingredients_filter = player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state
579 local config = getConfig(player.force)
581 text = string.lower(text)
582 -- NOTICE: localised name matching does not work at present, pending unlikely changes to Factorio API
583 for name, tech in pairs(player.force.technologies) do
584 if not tech.researched and tech.enabled then
585 local showtech = false
586 if string.find(string.lower(name), text, 1, true) then
587 -- show techs that match by name
589 -- elseif string.find(string.lower(game.technology_prototypes[name].localised_name), text, 1, true) then
590 -- -- show techs that match by localised name
593 for _, effect in pairs(tech.prototype.effects) do
594 if string.find(effect.type, text, 1, true) then
595 -- show techs that match by effect type
597 elseif effect.type == "unlock-recipe" then
598 if string.find(effect.recipe, text, 1, true) then
599 -- show techs that match by unlocked recipe name
601 -- elseif string.find(string.lower(game.recipe_prototypes[effect.recipe].localised_name), text, 1, true) then
602 -- -- show techs that match by unlocked recipe localised name
605 for _, product in pairs(prototypes.recipe[effect.recipe].products) do
606 if string.find(product.name, text, 1, true) then
607 -- show techs that match by unlocked recipe product name
609 -- elseif string.find(string.lower(game.item_prototypes[product.name].localised_name), text, 1, true) then
610 -- -- show techs that match by unlocked recipe product localised name
613 local prototype = prototypes.item[product.name]
615 if prototype.place_result then
616 if string.find(prototype.place_result.name, text, 1, true) then
617 -- show techs that match by unlocked recipe product placed entity name
619 -- elseif string.find(string.lower(game.entity_prototypes[prototype.place_result.name].localised_name), text, 1, true) then
620 -- -- show techs that match by unlocked recipe product placed entity localised name
623 elseif prototype.place_as_equipment_result then
624 if string.find(prototype.place_as_equipment_result.name, text, 1, true) then
625 -- show techs that match by unlocked recipe product placed equipment name
627 -- elseif string.find(string.lower(game.equipment_prototypes[prototype.place_as_equipment_result.name].localised_name), text, 1, true) then
628 -- -- show techs that match by unlocked recipe product placed equipment localised name
631 elseif prototype.place_as_tile_result then
632 if string.find(prototype.place_as_tile_result.result.name, text, 1, true) then
633 -- show techs that match by unlocked recipe product placed tile name
635 -- elseif string.find(string.lower(prototype.place_as_tile_result.result.localised_name), text, 1, true) then
636 -- -- show techs that match by unlocked recipe product placed tile localised name
647 if showtech and config.prioritized_techs then
648 for _, queued_tech in pairs(config.prioritized_techs) do
649 if name == queued_tech then
655 if showtech and config.deprioritized_techs then
656 for _, blacklisted_tech in pairs(config.deprioritized_techs) do
657 if name == blacklisted_tech then
663 if showtech and ingredients_filter then
664 for _, ingredient in pairs(tech.research_unit_ingredients) do
665 if not config.allowed_ingredients[ingredient.name] then
666 -- filter out techs that require disallowed ingredients (optional)
673 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
674 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. name, sprite = "auto_research_prioritize_top"}
675 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. name, sprite = "auto_research_prioritize_bottom"}
676 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_blacklist-" .. name, sprite = "auto_research_deprioritize"}
677 entryflow.add{type = "label", style = "auto_research_tech_label", name = name, caption = tech.localised_name}
678 for _, ingredient in pairs(tech.research_unit_ingredients) do
679 local sprite = "auto_research_tool_" .. ingredient.name
680 if not helpers.is_valid_sprite_path(sprite) then
681 sprite = "auto_research_unknown"
683 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
692 script.on_configuration_changed(function()
693 for _, force in pairs(game.forces) do
694 getConfig(force, true) -- triggers initialization of force config
697 script.on_event(defines.events.on_player_created, function(event)
698 local force = game.players[event.player_index].force
699 local config = getConfig(force) -- triggers initialization of force config
700 -- set any default queued/blacklisted techs
701 local queued_tech = settings.get_player_settings(game.players[event.player_index])["queued-tech-setting"].value
702 for tech in string.gmatch(queued_tech, "[^,$]+") do
703 tech = string.gsub(tech, "%s+", "")
704 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
705 table.insert(config.prioritized_techs, tech)
708 local blacklisted_tech = settings.get_player_settings(game.players[event.player_index])["blacklisted-tech-setting"].value
709 for tech in string.gmatch(blacklisted_tech, "[^,$]+") do
710 tech = string.gsub(tech, "%s+", "")
711 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
712 table.insert(config.deprioritized_techs, tech)
715 startNextResearch(force, true)
717 script.on_event(defines.events.on_force_created, function(event)
718 getConfig(event.force) -- triggers initialization of force config
720 script.on_event(defines.events.on_research_finished, onResearchFinished)
721 script.on_event(defines.events.on_gui_checked_state_changed, gui.onCheckboxClick)
722 script.on_event(defines.events.on_gui_click, gui.onClick)
723 script.on_event(defines.events.on_gui_text_changed, function(event)
724 if event.element.name ~= "auto_research_search_text" then
727 gui.updateSearchResult(game.players[event.player_index], event.element.text)
731 script.on_event("auto_research_toggle", function(event)
732 local player = game.players[event.player_index]
733 gui.toggleGui(player)
736 -- Add remote interfaces for enabling/disabling Auto Research
737 remote.add_interface("auto_research", {
738 enabled = function(forcename, value) setAutoResearch(game.forces[forcename], value) end,
739 queued_only = function(forcename, value) setQueuedOnly(game.forces[forcename], value) end,
740 allow_switching = function(forcename, value) setAllowSwitching(game.forces[forcename], value) end,
741 announce_completed = function(forcename, value) setAnnounceCompletedResearch(game.forces[forcename], value) end,
742 deprioritize_infinite_tech = function(forcename, value) setDeprioritizeInfiniteTech(game.forces[forcename], value) end