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 local research_strategies_left = research_strategies_outer.add{
300 style = "auto_research_list_flow",
301 name = "research_strategies_left",
302 direction = "vertical"
304 research_strategies_left.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"}
305 research_strategies_left.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_left.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"}
307 local research_strategies_right = research_strategies_outer.add{
309 style = "auto_research_list_flow",
310 name = "research_strategies_right",
311 direction = "vertical"
313 research_strategies_right.style.left_padding = 15
314 research_strategies_right.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_right.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"}
316 research_strategies_right.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"}
318 -- allowed ingredients
321 style = "auto_research_header_label",
322 caption = {"auto_research_gui.allowed_ingredients_label"}
324 local allowed_ingredients = frameflow.add{
326 style = "auto_research_list_flow",
327 name = "allowed_ingredients",
328 direction = "vertical"
330 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
335 style = "auto_research_header_label",
336 caption = {"auto_research_gui.prioritized_label"}
338 local prioritized = frameflow.add{
339 type = "scroll-pane",
340 name = "prioritized",
341 horizontal_scroll_policy = "never",
342 vertical_scroll_policy = "auto"
344 prioritized.style.top_padding = 5
345 prioritized.style.bottom_padding = 5
346 prioritized.style.maximal_height = 127
347 -- draw prioritized tech list
348 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
350 -- deprioritized techs
353 style = "auto_research_header_label",
354 caption = {"auto_research_gui.deprioritized_label"}
356 local deprioritized = frameflow.add{
357 type = "scroll-pane",
358 name = "deprioritized",
359 horizontal_scroll_policy = "never",
360 vertical_scroll_policy = "auto"
362 deprioritized.style.top_padding = 5
363 deprioritized.style.bottom_padding = 5
364 deprioritized.style.maximal_height = 127
365 -- draw deprioritized tech list
366 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
369 local searchflow = frameflow.add{
372 style = "auto_research_tech_flow",
373 direction = "horizontal"
377 style = "auto_research_header_label",
378 caption = {"auto_research_gui.search_label"}
382 name = "auto_research_search_text",
383 tooltip = {"auto_research_gui.search_tooltip"}
385 local searchoptionsflow = frameflow.add{
387 name = "searchoptionsflow",
388 style = "auto_research_tech_flow",
389 direction = "horizontal"
391 searchoptionsflow.add{
393 name = "auto_research_ingredients_filter_search_results",
394 caption = {"auto_research_gui.ingredients_filter_search_results"},
395 tooltip = {"auto_research_gui.ingredients_filter_search_results_tooltip"},
396 state = config.filter_search_results or false
398 local search = frameflow.add{
399 type = "scroll-pane",
401 horizontal_scroll_policy = "never",
402 vertical_scroll_policy = "auto"
404 search.style.top_padding = 5
405 search.style.bottom_padding = 5
406 search.style.maximal_height = 127
407 -- draw search result list
408 gui.updateSearchResult(player, "")
412 onCheckboxClick = function(event)
413 local player = game.players[event.player_index]
414 local force = player.force
415 local name = event.element.name
416 if name == "auto_research_enabled" then
417 setAutoResearch(force, event.element.state)
418 elseif name == "auto_research_queued_only" then
419 setQueuedOnly(force, event.element.state)
420 elseif name == "auto_research_allow_switching" then
421 setAllowSwitching(force, event.element.state)
422 elseif name == "auto_research_announce_completed" then
423 setAnnounceCompletedResearch(force, event.element.state)
424 elseif name == "auto_research_deprioritize_infinite_tech" then
425 setDeprioritizeInfiniteTech(force, event.element.state)
426 elseif name == "auto_research_ingredients_filter_search_results" then
427 local config = getConfig(force)
428 config.filter_search_results = event.element.state
429 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
433 onClick = function(event)
434 local player = game.players[event.player_index]
435 local force = player.force
436 local config = getConfig(force)
437 local name = event.element.name
438 if name == "auto_research_search_text" then
439 if event.button == defines.mouse_button_type.right then
440 player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text = ""
441 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
443 elseif string.find(name, "auto_research_research") then
444 config.research_strategy = string.match(name, "^auto_research_research_(.*)$")
445 player.gui.top.auto_research_gui.flow.research_strategies_outer.research_strategies_left.auto_research_research_fast.state = (config.research_strategy == "fast")
446 player.gui.top.auto_research_gui.flow.research_strategies_outer.research_strategies_left.auto_research_research_cheap.state = (config.research_strategy == "cheap")
447 player.gui.top.auto_research_gui.flow.research_strategies_outer.research_strategies_left.auto_research_research_balanced.state = (config.research_strategy == "balanced")
448 player.gui.top.auto_research_gui.flow.research_strategies_outer.research_strategies_right.auto_research_research_slow.state = (config.research_strategy == "slow")
449 player.gui.top.auto_research_gui.flow.research_strategies_outer.research_strategies_right.auto_research_research_expensive.state = (config.research_strategy == "expensive")
450 player.gui.top.auto_research_gui.flow.research_strategies_outer.research_strategies_right.auto_research_research_random.state = (config.research_strategy == "random")
451 -- start new research
452 startNextResearch(force)
454 local prefix, name = string.match(name, "^auto_research_([^-]*)-(.*)$")
455 if prefix == "allow_ingredient" then
456 config.allowed_ingredients[name] = not config.allowed_ingredients[name]
457 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
458 if player.gui.top.auto_research_gui.flow.searchoptionsflow.auto_research_ingredients_filter_search_results.state then
459 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
461 startNextResearch(force)
462 elseif name and force.technologies[name] then
463 -- remove tech from prioritized list
464 for i = #config.prioritized_techs, 1, -1 do
465 if config.prioritized_techs[i] == name then
466 table.remove(config.prioritized_techs, i)
469 -- and from deprioritized list
470 for i = #config.deprioritized_techs, 1, -1 do
471 if config.deprioritized_techs[i] == name then
472 table.remove(config.deprioritized_techs, i)
475 if prefix == "queue_top" then
476 -- add tech to top of prioritized list
477 table.insert(config.prioritized_techs, 1, name)
478 elseif prefix == "queue_bottom" then
479 -- add tech to bottom of prioritized list
480 table.insert(config.prioritized_techs, name)
481 elseif prefix == "blacklist" then
482 -- add tech to list of deprioritized techs
483 table.insert(config.deprioritized_techs, name)
485 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
486 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
487 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
489 -- start new research
490 startNextResearch(force)
495 updateAllowedIngredientsList = function(flow, player, config)
497 while flow["flow" .. counter] do
498 flow["flow" .. counter].destroy()
499 counter = counter + 1
502 for ingredientname, allowed in pairs(config.allowed_ingredients) do
503 local flowname = "flow" .. math.floor(counter / 10) + 1
504 local ingredientflow = flow[flowname]
505 if not ingredientflow then
506 ingredientflow = flow.add {
508 style = "auto_research_tech_flow",
510 direction = "horizontal"
513 local sprite = "auto_research_tool_" .. ingredientname
514 if not player.gui.is_valid_sprite_path(sprite) then
515 sprite = "auto_research_unknown"
517 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}
518 counter = counter + 1
522 updateTechnologyList = function(scrollpane, technologies, player, show_queue_buttons)
523 if scrollpane.flow then
524 scrollpane.flow.destroy()
526 local flow = scrollpane.add{
528 style = "auto_research_list_flow",
530 direction = "vertical"
532 if #technologies > 0 then
533 for _, techname in pairs(technologies) do
534 local tech = player.force.technologies[techname]
536 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
537 if show_queue_buttons then
538 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. techname, sprite = "auto_research_prioritize_top"}
539 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. techname, sprite = "auto_research_prioritize_bottom"}
541 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_delete-" .. techname, sprite = "auto_research_delete"}
542 entryflow.add{type = "label", style = "auto_research_tech_label", caption = tech.localised_name}
543 for _, ingredient in pairs(tech.research_unit_ingredients) do
544 local sprite = "auto_research_tool_" .. ingredient.name
545 if not player.gui.is_valid_sprite_path(sprite) then
546 sprite = "auto_research_unknown"
548 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
553 local entryflow = flow.add{type = "flow", direction = "horizontal"}
554 entryflow.add{type = "label", caption = {"auto_research_gui.none"}}
558 updateSearchResult = function(player, text)
559 local scrollpane = player.gui.top.auto_research_gui.flow.search
560 if scrollpane.flow then
561 scrollpane.flow.destroy()
563 local flow = scrollpane.add{
565 style = "auto_research_list_flow",
567 direction = "vertical"
569 local ingredients_filter = player.gui.top.auto_research_gui.flow.searchoptionsflow.auto_research_ingredients_filter_search_results.state
570 local config = getConfig(player.force)
572 text = string.lower(text)
573 -- NOTICE: localised name matching does not work at present, pending unlikely changes to Factorio API
574 for name, tech in pairs(player.force.technologies) do
575 if not tech.researched and tech.enabled then
576 local showtech = false
577 if string.find(string.lower(name), text, 1, true) then
578 -- show techs that match by name
580 -- elseif string.find(string.lower(game.technology_prototypes[name].localised_name), text, 1, true) then
581 -- -- show techs that match by localised name
584 for _, effect in pairs(tech.effects) do
585 if string.find(effect.type, text, 1, true) then
586 -- show techs that match by effect type
588 elseif effect.type == "unlock-recipe" then
589 if string.find(effect.recipe, text, 1, true) then
590 -- show techs that match by unlocked recipe name
592 -- elseif string.find(string.lower(game.recipe_prototypes[effect.recipe].localised_name), text, 1, true) then
593 -- -- show techs that match by unlocked recipe localised name
596 for _, product in pairs(game.recipe_prototypes[effect.recipe].products) do
597 if string.find(product.name, text, 1, true) then
598 -- show techs that match by unlocked recipe product name
600 -- elseif string.find(string.lower(game.item_prototypes[product.name].localised_name), text, 1, true) then
601 -- -- show techs that match by unlocked recipe product localised name
604 local prototype = game.item_prototypes[product.name]
606 if prototype.place_result then
607 if string.find(prototype.place_result.name, text, 1, true) then
608 -- show techs that match by unlocked recipe product placed entity name
610 -- elseif string.find(string.lower(game.entity_prototypes[prototype.place_result.name].localised_name), text, 1, true) then
611 -- -- show techs that match by unlocked recipe product placed entity localised name
614 elseif prototype.place_as_equipment_result then
615 if string.find(prototype.place_as_equipment_result.name, text, 1, true) then
616 -- show techs that match by unlocked recipe product placed equipment name
618 -- elseif string.find(string.lower(game.equipment_prototypes[prototype.place_as_equipment_result.name].localised_name), text, 1, true) then
619 -- -- show techs that match by unlocked recipe product placed equipment localised name
622 elseif prototype.place_as_tile_result then
623 if string.find(prototype.place_as_tile_result.result.name, text, 1, true) then
624 -- show techs that match by unlocked recipe product placed tile name
626 -- elseif string.find(string.lower(prototype.place_as_tile_result.result.localised_name), text, 1, true) then
627 -- -- show techs that match by unlocked recipe product placed tile localised name
638 if showtech and config.prioritized_techs then
639 for _, queued_tech in pairs(config.prioritized_techs) do
640 if name == queued_tech then
646 if showtech and config.deprioritized_techs then
647 for _, blacklisted_tech in pairs(config.deprioritized_techs) do
648 if name == blacklisted_tech then
654 if showtech and ingredients_filter then
655 for _, ingredient in pairs(tech.research_unit_ingredients) do
656 if not config.allowed_ingredients[ingredient.name] then
657 -- filter out techs that require disallowed ingredients (optional)
664 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
665 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. name, sprite = "auto_research_prioritize_top"}
666 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. name, sprite = "auto_research_prioritize_bottom"}
667 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_blacklist-" .. name, sprite = "auto_research_deprioritize"}
668 entryflow.add{type = "label", style = "auto_research_tech_label", name = name, caption = tech.localised_name}
669 for _, ingredient in pairs(tech.research_unit_ingredients) do
670 local sprite = "auto_research_tool_" .. ingredient.name
671 if not player.gui.is_valid_sprite_path(sprite) then
672 sprite = "auto_research_unknown"
674 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
683 script.on_configuration_changed(function()
684 for _, force in pairs(game.forces) do
685 getConfig(force, true) -- triggers initialization of force config
688 script.on_event(defines.events.on_player_created, function(event)
689 local force = game.players[event.player_index].force
690 local config = getConfig(force) -- triggers initialization of force config
691 -- set any default queued/blacklisted techs
692 local queued_tech = settings.get_player_settings(game.players[event.player_index])["queued-tech-setting"].value
693 for tech in string.gmatch(queued_tech, "[^,$]+") do
694 tech = string.gsub(tech, "%s+", "")
695 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
696 table.insert(config.prioritized_techs, tech)
699 local blacklisted_tech = settings.get_player_settings(game.players[event.player_index])["blacklisted-tech-setting"].value
700 for tech in string.gmatch(blacklisted_tech, "[^,$]+") do
701 tech = string.gsub(tech, "%s+", "")
702 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
703 table.insert(config.deprioritized_techs, tech)
706 startNextResearch(force, true)
708 script.on_event(defines.events.on_force_created, function(event)
709 getConfig(event.force) -- triggers initialization of force config
711 script.on_event(defines.events.on_research_finished, onResearchFinished)
712 script.on_event(defines.events.on_gui_checked_state_changed, gui.onCheckboxClick)
713 script.on_event(defines.events.on_gui_click, gui.onClick)
714 script.on_event(defines.events.on_gui_text_changed, function(event)
715 if event.element.name ~= "auto_research_search_text" then
718 gui.updateSearchResult(game.players[event.player_index], event.element.text)
722 script.on_event("auto_research_toggle", function(event)
723 local player = game.players[event.player_index]
724 gui.toggleGui(player)
727 -- Add remote interfaces for enabling/disabling Auto Research
728 remote.add_interface("auto_research", {
729 enabled = function(forcename, value) setAutoResearch(game.forces[forcename], value) end,
730 queued_only = function(forcename, value) setQueuedOnly(game.forces[forcename], value) end,
731 allow_switching = function(forcename, value) setAllowSwitching(game.forces[forcename], value) end,
732 announce_completed = function(forcename, value) setAnnounceCompletedResearch(game.forces[forcename], value) end,
733 deprioritize_infinite_tech = function(forcename, value) setDeprioritizeInfiniteTech(game.forces[forcename], value) end