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 -- keep queue, just put next_research at the start.
219 if next_research then
221 table.insert(rq, next_research)
224 if force.research_queue[i] == nil then break end
225 if not (force.current_research and config.allow_switching and i == 1) then
226 table.insert(rq, force.research_queue[i].name)
230 force.research_queue = rq
234 function onResearchFinished(event)
235 local force = event.research.force
236 local config = getConfig(force)
237 -- remove researched stuff from prioritized_techs and deprioritized_techs
238 for i = #config.prioritized_techs, 1, -1 do
239 local tech = force.technologies[config.prioritized_techs[i]]
240 if not tech or tech.researched then
241 table.remove(config.prioritized_techs, i)
244 for i = #config.deprioritized_techs, 1, -1 do
245 local tech = force.technologies[config.deprioritized_techs[i]]
246 if not tech or tech.researched then
247 table.remove(config.deprioritized_techs, i)
250 -- announce completed research
251 if config.announce_completed and config.no_announce_this_tick ~= game.tick then
252 if config.last_research_finish_tick == game.tick then
253 config.no_announce_this_tick = game.tick
256 if event.research.research_unit_count_formula then
257 level = (event.research.researched and event.research.level) or (event.research.level - 1)
259 force.print{"auto_research.announce_completed", event.research.localised_name, level}
263 startNextResearch(event.research.force)
268 toggleGui = function(player)
269 if player.gui.top.auto_research_gui then
270 player.gui.top.auto_research_gui.destroy()
272 local force = player.force
273 local config = getConfig(force)
274 local frame = player.gui.top.add{
276 name = "auto_research_gui",
277 direction = "vertical",
278 caption = {"auto_research_gui.title"}
280 local frameflow = frame.add{
282 style = "auto_research_list_flow",
284 direction = "vertical"
288 frameflow.add{type = "checkbox", name = "auto_research_enabled", caption = {"auto_research_gui.enabled"}, tooltip = {"auto_research_gui.enabled_tooltip"}, state = config.enabled or false}
289 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}
290 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}
291 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}
292 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}
297 style = "auto_research_header_label",
298 caption = {"auto_research_gui.research_strategy"}
300 local research_strategies_outer = frameflow.add{
302 style = "auto_research_tech_flow",
303 name = "research_strategies_outer",
304 direction = "horizontal"
306 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"}
307 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"}
308 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"}
309 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"}
310 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"}
311 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"}
313 research_strategies_outer.style.horizontal_spacing = 6
315 -- allowed ingredients
318 style = "auto_research_header_label",
319 caption = {"auto_research_gui.allowed_ingredients_label"}
321 local allowed_ingredients = frameflow.add{
323 style = "auto_research_list_flow",
324 name = "allowed_ingredients",
325 direction = "vertical"
327 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
332 style = "auto_research_header_label",
333 caption = {"auto_research_gui.prioritized_label"}
335 local prioritized = frameflow.add{
336 type = "scroll-pane",
337 name = "prioritized",
338 horizontal_scroll_policy = "never",
339 vertical_scroll_policy = "auto"
341 prioritized.style.top_padding = 5
342 prioritized.style.bottom_padding = 5
343 prioritized.style.maximal_height = 127
344 prioritized.style.minimal_width = 440
345 -- draw prioritized tech list
346 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
348 -- deprioritized techs
351 style = "auto_research_header_label",
352 caption = {"auto_research_gui.deprioritized_label"}
354 local deprioritized = frameflow.add{
355 type = "scroll-pane",
356 name = "deprioritized",
357 horizontal_scroll_policy = "never",
358 vertical_scroll_policy = "auto"
360 deprioritized.style.top_padding = 5
361 deprioritized.style.bottom_padding = 5
362 deprioritized.style.maximal_height = 127
363 deprioritized.style.minimal_width = 440
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"}
387 name = "auto_research_ingredients_filter_search_results",
388 caption = {"auto_research_gui.ingredients_filter_search_results"},
389 tooltip = {"auto_research_gui.ingredients_filter_search_results_tooltip"},
390 state = config.filter_search_results or false
392 searchflow.style.horizontal_spacing = 6
393 searchflow.style.vertical_align = "center"
395 local search = frameflow.add{
396 type = "scroll-pane",
398 horizontal_scroll_policy = "never",
399 vertical_scroll_policy = "auto"
401 search.style.top_padding = 5
402 search.style.bottom_padding = 5
403 search.style.maximal_height = 127
404 search.style.minimal_width = 440
406 -- draw search result list
407 gui.updateSearchResult(player, "")
411 onCheckboxClick = function(event)
412 local player = game.players[event.player_index]
413 local force = player.force
414 local name = event.element.name
415 if name == "auto_research_enabled" then
416 setAutoResearch(force, event.element.state)
417 elseif name == "auto_research_queued_only" then
418 setQueuedOnly(force, event.element.state)
419 elseif name == "auto_research_allow_switching" then
420 setAllowSwitching(force, event.element.state)
421 elseif name == "auto_research_announce_completed" then
422 setAnnounceCompletedResearch(force, event.element.state)
423 elseif name == "auto_research_deprioritize_infinite_tech" then
424 setDeprioritizeInfiniteTech(force, event.element.state)
425 elseif name == "auto_research_ingredients_filter_search_results" then
426 local config = getConfig(force)
427 config.filter_search_results = event.element.state
428 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
432 onClick = function(event)
433 local player = game.players[event.player_index]
434 local force = player.force
435 local config = getConfig(force)
436 local name = event.element.name
437 if name == "auto_research_search_text" then
438 if event.button == defines.mouse_button_type.right then
439 player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text = ""
440 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
442 elseif string.find(name, "auto_research_research") then
443 config.research_strategy = string.match(name, "^auto_research_research_(.*)$")
444 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_fast.state = (config.research_strategy == "fast")
445 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_cheap.state = (config.research_strategy == "cheap")
446 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_balanced.state = (config.research_strategy == "balanced")
447 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_slow.state = (config.research_strategy == "slow")
448 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_expensive.state = (config.research_strategy == "expensive")
449 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_random.state = (config.research_strategy == "random")
450 -- start new research
451 startNextResearch(force)
453 local prefix, name = string.match(name, "^auto_research_([^-]*)-(.*)$")
454 if prefix == "allow_ingredient" then
455 config.allowed_ingredients[name] = not config.allowed_ingredients[name]
456 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
457 if player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state then
458 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
460 startNextResearch(force)
461 elseif name and force.technologies[name] then
462 -- remove tech from prioritized list
463 for i = #config.prioritized_techs, 1, -1 do
464 if config.prioritized_techs[i] == name then
465 table.remove(config.prioritized_techs, i)
468 -- and from deprioritized list
469 for i = #config.deprioritized_techs, 1, -1 do
470 if config.deprioritized_techs[i] == name then
471 table.remove(config.deprioritized_techs, i)
474 if prefix == "queue_top" then
475 -- add tech to top of prioritized list
476 table.insert(config.prioritized_techs, 1, name)
477 elseif prefix == "queue_bottom" then
478 -- add tech to bottom of prioritized list
479 table.insert(config.prioritized_techs, name)
480 elseif prefix == "blacklist" then
481 -- add tech to list of deprioritized techs
482 table.insert(config.deprioritized_techs, name)
484 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
485 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
486 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
488 -- start new research
489 startNextResearch(force)
494 updateAllowedIngredientsList = function(flow, player, config)
496 while flow["flow" .. counter] do
497 flow["flow" .. counter].destroy()
498 counter = counter + 1
501 for ingredientname, allowed in pairs(config.allowed_ingredients) do
502 local flowname = "flow" .. math.floor(counter / 10) + 1
503 local ingredientflow = flow[flowname]
504 if not ingredientflow then
505 ingredientflow = flow.add {
507 style = "auto_research_tech_flow",
509 direction = "horizontal"
512 local sprite = "auto_research_tool_" .. ingredientname
513 if not helpers.is_valid_sprite_path(sprite) then
514 sprite = "auto_research_unknown"
516 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}
517 counter = counter + 1
521 updateTechnologyList = function(scrollpane, technologies, player, show_queue_buttons)
522 if scrollpane.flow then
523 scrollpane.flow.destroy()
525 local flow = scrollpane.add{
527 style = "auto_research_list_flow",
529 direction = "vertical"
531 if #technologies > 0 then
532 for _, techname in pairs(technologies) do
533 local tech = player.force.technologies[techname]
535 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
536 if show_queue_buttons then
537 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. techname, sprite = "auto_research_prioritize_top"}
538 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. techname, sprite = "auto_research_prioritize_bottom"}
540 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_delete-" .. techname, sprite = "auto_research_delete"}
541 entryflow.add{type = "label", style = "auto_research_tech_label", caption = tech.localised_name}
542 for _, ingredient in pairs(tech.research_unit_ingredients) do
543 local sprite = "auto_research_tool_" .. ingredient.name
544 if not helpers.is_valid_sprite_path(sprite) then
545 sprite = "auto_research_unknown"
547 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
552 local entryflow = flow.add{type = "flow", direction = "horizontal"}
553 entryflow.add{type = "label", caption = {"auto_research_gui.none"}}
557 updateSearchResult = function(player, text)
558 local scrollpane = player.gui.top.auto_research_gui.flow.search
559 if scrollpane.flow then
560 scrollpane.flow.destroy()
562 local flow = scrollpane.add{
564 style = "auto_research_list_flow",
566 direction = "vertical"
568 local ingredients_filter = player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state
569 local config = getConfig(player.force)
571 text = string.lower(text)
572 -- NOTICE: localised name matching does not work at present, pending unlikely changes to Factorio API
573 for name, tech in pairs(player.force.technologies) do
574 if not tech.researched and tech.enabled then
575 local showtech = false
576 if string.find(string.lower(name), text, 1, true) then
577 -- show techs that match by name
579 -- elseif string.find(string.lower(game.technology_prototypes[name].localised_name), text, 1, true) then
580 -- -- show techs that match by localised name
583 for _, effect in pairs(tech.prototype.effects) do
584 if string.find(effect.type, text, 1, true) then
585 -- show techs that match by effect type
587 elseif effect.type == "unlock-recipe" then
588 if string.find(effect.recipe, text, 1, true) then
589 -- show techs that match by unlocked recipe name
591 -- elseif string.find(string.lower(game.recipe_prototypes[effect.recipe].localised_name), text, 1, true) then
592 -- -- show techs that match by unlocked recipe localised name
595 for _, product in pairs(prototypes.recipe[effect.recipe].products) do
596 if string.find(product.name, text, 1, true) then
597 -- show techs that match by unlocked recipe product name
599 -- elseif string.find(string.lower(game.item_prototypes[product.name].localised_name), text, 1, true) then
600 -- -- show techs that match by unlocked recipe product localised name
603 local prototype = prototypes.item[product.name]
605 if prototype.place_result then
606 if string.find(prototype.place_result.name, text, 1, true) then
607 -- show techs that match by unlocked recipe product placed entity name
609 -- elseif string.find(string.lower(game.entity_prototypes[prototype.place_result.name].localised_name), text, 1, true) then
610 -- -- show techs that match by unlocked recipe product placed entity localised name
613 elseif prototype.place_as_equipment_result then
614 if string.find(prototype.place_as_equipment_result.name, text, 1, true) then
615 -- show techs that match by unlocked recipe product placed equipment name
617 -- elseif string.find(string.lower(game.equipment_prototypes[prototype.place_as_equipment_result.name].localised_name), text, 1, true) then
618 -- -- show techs that match by unlocked recipe product placed equipment localised name
621 elseif prototype.place_as_tile_result then
622 if string.find(prototype.place_as_tile_result.result.name, text, 1, true) then
623 -- show techs that match by unlocked recipe product placed tile name
625 -- elseif string.find(string.lower(prototype.place_as_tile_result.result.localised_name), text, 1, true) then
626 -- -- show techs that match by unlocked recipe product placed tile localised name
637 if showtech and config.prioritized_techs then
638 for _, queued_tech in pairs(config.prioritized_techs) do
639 if name == queued_tech then
645 if showtech and config.deprioritized_techs then
646 for _, blacklisted_tech in pairs(config.deprioritized_techs) do
647 if name == blacklisted_tech then
653 if showtech and ingredients_filter then
654 for _, ingredient in pairs(tech.research_unit_ingredients) do
655 if not config.allowed_ingredients[ingredient.name] then
656 -- filter out techs that require disallowed ingredients (optional)
663 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
664 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. name, sprite = "auto_research_prioritize_top"}
665 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. name, sprite = "auto_research_prioritize_bottom"}
666 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_blacklist-" .. name, sprite = "auto_research_deprioritize"}
667 entryflow.add{type = "label", style = "auto_research_tech_label", name = name, caption = tech.localised_name}
668 for _, ingredient in pairs(tech.research_unit_ingredients) do
669 local sprite = "auto_research_tool_" .. ingredient.name
670 if not helpers.is_valid_sprite_path(sprite) then
671 sprite = "auto_research_unknown"
673 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
682 script.on_configuration_changed(function()
683 for _, force in pairs(game.forces) do
684 getConfig(force, true) -- triggers initialization of force config
687 script.on_event(defines.events.on_player_created, function(event)
688 local force = game.players[event.player_index].force
689 local config = getConfig(force) -- triggers initialization of force config
690 -- set any default queued/blacklisted techs
691 local queued_tech = settings.get_player_settings(game.players[event.player_index])["queued-tech-setting"].value
692 for tech in string.gmatch(queued_tech, "[^,$]+") do
693 tech = string.gsub(tech, "%s+", "")
694 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
695 table.insert(config.prioritized_techs, tech)
698 local blacklisted_tech = settings.get_player_settings(game.players[event.player_index])["blacklisted-tech-setting"].value
699 for tech in string.gmatch(blacklisted_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.deprioritized_techs, tech)
705 startNextResearch(force, true)
707 script.on_event(defines.events.on_force_created, function(event)
708 getConfig(event.force) -- triggers initialization of force config
710 script.on_event(defines.events.on_research_finished, onResearchFinished)
711 script.on_event(defines.events.on_gui_checked_state_changed, gui.onCheckboxClick)
712 script.on_event(defines.events.on_gui_click, gui.onClick)
713 script.on_event(defines.events.on_gui_text_changed, function(event)
714 if event.element.name ~= "auto_research_search_text" then
717 gui.updateSearchResult(game.players[event.player_index], event.element.text)
721 script.on_event("auto_research_toggle", function(event)
722 local player = game.players[event.player_index]
723 gui.toggleGui(player)
726 -- Add remote interfaces for enabling/disabling Auto Research
727 remote.add_interface("auto_research", {
728 enabled = function(forcename, value) setAutoResearch(game.forces[forcename], value) end,
729 queued_only = function(forcename, value) setQueuedOnly(game.forces[forcename], value) end,
730 allow_switching = function(forcename, value) setAllowSwitching(game.forces[forcename], value) end,
731 announce_completed = function(forcename, value) setAnnounceCompletedResearch(game.forces[forcename], value) end,
732 deprioritize_infinite_tech = function(forcename, value) setDeprioritizeInfiniteTech(game.forces[forcename], value) end