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
159 -- function for calculating tech effort
160 function calcEffort(tech, config)
161 local ingredientCount = function(ingredients)
162 local tech_ingredients = 0
163 for _, ingredient in pairs(tech.research_unit_ingredients) do
164 tech_ingredients = tech_ingredients + ingredient.amount
166 return tech_ingredients
169 if config.research_strategy == "fast" then
170 effort = math.max(tech.research_unit_energy, 1) * math.max(tech.research_unit_count, 1)
171 elseif config.research_strategy == "slow" then
172 effort = math.max(tech.research_unit_energy, 1) * math.max(tech.research_unit_count, 1) * -1
173 elseif config.research_strategy == "cheap" then
174 effort = math.max(ingredientCount(tech.research_unit_ingredients), 1) * math.max(tech.research_unit_count, 1)
175 elseif config.research_strategy == "expensive" then
176 effort = math.max(ingredientCount(tech.research_unit_ingredients), 1) * math.max(tech.research_unit_count, 1) * -1
177 elseif config.research_strategy == "balanced" then
178 effort = math.max(tech.research_unit_count, 1) * math.max(tech.research_unit_energy, 1) * math.max(ingredientCount(tech.research_unit_ingredients), 1)
180 effort = math.random(1, 999)
182 if (config.deprioritize_infinite_tech and config.infinite_research[tech.name]) then
183 return effort * (effort > 0 and 1000 or -1000)
189 function sortTechsByEffort(techs, config)
190 local compare = function(a, b)
191 return calcEffort(a[2], config) < calcEffort(b[2], config)
193 if config.research_strategy ~= "random" then
194 table.sort(techs, compare)
198 function startNextResearch(force, override_spam_detection)
199 local config = getConfig(force)
200 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
203 config.last_research_finish_tick = game.tick -- if multiple research finish same tick for same force, the user probably enabled all techs
205 -- see if there are some techs we should research first
206 local next_research = nil
207 local least_effort = nil
208 for _, techname in pairs(config.prioritized_techs) do
209 local tech = force.technologies[techname]
210 if tech and not next_research then
211 local pretechs = getPretechs(tech)
212 for _, pretech in pairs(pretechs) do
213 local effort = calcEffort(pretech, config)
214 if (not least_effort or effort < least_effort) and canResearch(force, pretech, config) then
215 next_research = pretech.name
216 least_effort = effort
222 -- if no queued tech should be researched then research the "least effort" tech not researched yet
223 if not config.prioritized_only and not next_research then
224 for techname, tech in pairs(force.technologies) do
225 if tech.enabled and not tech.researched then
226 local effort = calcEffort(tech, config)
227 if (not least_effort or effort < least_effort) and canResearch(force, tech, config) then
228 next_research = techname
229 least_effort = effort
235 -- keep queue, just put next_research at the start.
236 if next_research then
239 -- manage last element of research queue
241 if force.research_queue[i] == nil then break end
242 if i == (#force.research_queue) then
243 if not (force.current_research and config.allow_switching) then
244 table.insert(rq, force.research_queue[i].name)
247 table.insert(rq, force.research_queue[i].name)
250 table.insert(rq, next_research)
252 force.research_queue = rq
256 function onResearchFinished(event)
257 local force = event.research.force
258 local config = getConfig(force)
259 -- remove researched stuff from prioritized_techs and deprioritized_techs
260 for i = #config.prioritized_techs, 1, -1 do
261 local tech = force.technologies[config.prioritized_techs[i]]
262 if not tech or tech.researched then
263 table.remove(config.prioritized_techs, i)
266 for i = #config.deprioritized_techs, 1, -1 do
267 local tech = force.technologies[config.deprioritized_techs[i]]
268 if not tech or tech.researched then
269 table.remove(config.deprioritized_techs, i)
272 -- announce completed research
273 if config.announce_completed and config.no_announce_this_tick ~= game.tick then
274 if config.last_research_finish_tick == game.tick then
275 config.no_announce_this_tick = game.tick
278 if event.research.research_unit_count_formula then
279 level = (event.research.researched and event.research.level) or (event.research.level - 1)
281 force.print{"auto_research.announce_completed", event.research.localised_name, level}
285 startNextResearch(event.research.force)
290 toggleGui = function(player)
291 if player.gui.top.auto_research_gui then
292 player.gui.top.auto_research_gui.destroy()
294 local force = player.force
295 local config = getConfig(force)
296 local frame = player.gui.top.add{
298 name = "auto_research_gui",
299 direction = "vertical",
300 caption = {"auto_research_gui.title"}
302 local frameflow = frame.add{
304 style = "auto_research_list_flow",
306 direction = "vertical"
310 frameflow.add{type = "checkbox", name = "auto_research_enabled", caption = {"auto_research_gui.enabled"}, tooltip = {"auto_research_gui.enabled_tooltip"}, state = config.enabled or false}
311 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}
312 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}
313 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}
314 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}
319 style = "auto_research_header_label",
320 caption = {"auto_research_gui.research_strategy"}
322 local research_strategies_outer = frameflow.add{
324 style = "auto_research_tech_flow",
325 name = "research_strategies_outer",
326 direction = "horizontal"
328 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"}
329 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"}
330 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"}
331 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"}
332 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"}
333 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"}
335 research_strategies_outer.style.horizontal_spacing = 6
337 -- allowed ingredients
340 style = "auto_research_header_label",
341 caption = {"auto_research_gui.allowed_ingredients_label"}
343 local allowed_ingredients = frameflow.add{
345 style = "auto_research_list_flow",
346 name = "allowed_ingredients",
347 direction = "vertical"
349 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
354 style = "auto_research_header_label",
355 caption = {"auto_research_gui.prioritized_label"}
357 local prioritized = frameflow.add{
358 type = "scroll-pane",
359 name = "prioritized",
360 horizontal_scroll_policy = "never",
361 vertical_scroll_policy = "auto"
363 prioritized.style.top_padding = 5
364 prioritized.style.bottom_padding = 5
365 prioritized.style.maximal_height = 127
366 prioritized.style.minimal_width = 440
367 -- draw prioritized tech list
368 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
370 -- deprioritized techs
373 style = "auto_research_header_label",
374 caption = {"auto_research_gui.deprioritized_label"}
376 local deprioritized = frameflow.add{
377 type = "scroll-pane",
378 name = "deprioritized",
379 horizontal_scroll_policy = "never",
380 vertical_scroll_policy = "auto"
382 deprioritized.style.top_padding = 5
383 deprioritized.style.bottom_padding = 5
384 deprioritized.style.maximal_height = 127
385 deprioritized.style.minimal_width = 440
387 -- draw deprioritized tech list
388 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
391 local searchflow = frameflow.add{
394 style = "auto_research_tech_flow",
395 direction = "horizontal"
399 style = "auto_research_header_label",
400 caption = {"auto_research_gui.search_label"}
404 name = "auto_research_search_text",
405 tooltip = {"auto_research_gui.search_tooltip"}
409 name = "auto_research_ingredients_filter_search_results",
410 caption = {"auto_research_gui.ingredients_filter_search_results"},
411 tooltip = {"auto_research_gui.ingredients_filter_search_results_tooltip"},
412 state = config.filter_search_results or false
414 searchflow.style.horizontal_spacing = 6
415 searchflow.style.vertical_align = "center"
417 local search = frameflow.add{
418 type = "scroll-pane",
420 horizontal_scroll_policy = "never",
421 vertical_scroll_policy = "auto"
423 search.style.top_padding = 5
424 search.style.bottom_padding = 5
425 search.style.maximal_height = 127
426 search.style.minimal_width = 440
428 -- draw search result list
429 gui.updateSearchResult(player, "")
433 onCheckboxClick = function(event)
434 local player = game.players[event.player_index]
435 local force = player.force
436 local name = event.element.name
437 if name == "auto_research_enabled" then
438 setAutoResearch(force, event.element.state)
439 elseif name == "auto_research_queued_only" then
440 setQueuedOnly(force, event.element.state)
441 elseif name == "auto_research_allow_switching" then
442 setAllowSwitching(force, event.element.state)
443 elseif name == "auto_research_announce_completed" then
444 setAnnounceCompletedResearch(force, event.element.state)
445 elseif name == "auto_research_deprioritize_infinite_tech" then
446 setDeprioritizeInfiniteTech(force, event.element.state)
447 elseif name == "auto_research_ingredients_filter_search_results" then
448 local config = getConfig(force)
449 config.filter_search_results = event.element.state
450 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
454 onClick = function(event)
455 local player = game.players[event.player_index]
456 local force = player.force
457 local config = getConfig(force)
458 local name = event.element.name
459 if name == "auto_research_search_text" then
460 if event.button == defines.mouse_button_type.right then
461 player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text = ""
462 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
464 elseif string.find(name, "auto_research_research") then
465 config.research_strategy = string.match(name, "^auto_research_research_(.*)$")
466 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_fast.state = (config.research_strategy == "fast")
467 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_cheap.state = (config.research_strategy == "cheap")
468 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_balanced.state = (config.research_strategy == "balanced")
469 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_slow.state = (config.research_strategy == "slow")
470 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_expensive.state = (config.research_strategy == "expensive")
471 player.gui.top.auto_research_gui.flow.research_strategies_outer.auto_research_research_random.state = (config.research_strategy == "random")
473 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
474 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
475 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
477 -- start new research
478 startNextResearch(force)
480 local prefix, name = string.match(name, "^auto_research_([^-]*)-(.*)$")
481 if prefix == "allow_ingredient" then
482 config.allowed_ingredients[name] = not config.allowed_ingredients[name]
483 gui.updateAllowedIngredientsList(player.gui.top.auto_research_gui.flow.allowed_ingredients, player, config)
484 if player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state then
485 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
487 startNextResearch(force)
488 elseif name and force.technologies[name] then
489 -- remove tech from prioritized list
490 for i = #config.prioritized_techs, 1, -1 do
491 if config.prioritized_techs[i] == name then
492 table.remove(config.prioritized_techs, i)
495 -- and from deprioritized list
496 for i = #config.deprioritized_techs, 1, -1 do
497 if config.deprioritized_techs[i] == name then
498 table.remove(config.deprioritized_techs, i)
501 if prefix == "queue_top" then
502 -- add tech to top of prioritized list
503 table.insert(config.prioritized_techs, 1, name)
504 elseif prefix == "queue_bottom" then
505 -- add tech to bottom of prioritized list
506 table.insert(config.prioritized_techs, name)
507 elseif prefix == "blacklist" then
508 -- add tech to list of deprioritized techs
509 table.insert(config.deprioritized_techs, name)
511 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.prioritized, config.prioritized_techs, player, true)
512 gui.updateTechnologyList(player.gui.top.auto_research_gui.flow.deprioritized, config.deprioritized_techs, player)
513 gui.updateSearchResult(player, player.gui.top.auto_research_gui.flow.searchflow.auto_research_search_text.text)
515 -- start new research
516 startNextResearch(force)
521 updateAllowedIngredientsList = function(flow, player, config)
523 while flow["flow" .. counter] do
524 flow["flow" .. counter].destroy()
525 counter = counter + 1
528 for ingredientname, allowed in pairs(config.allowed_ingredients) do
529 local flowname = "flow" .. math.floor(counter / 10) + 1
530 local ingredientflow = flow[flowname]
531 if not ingredientflow then
532 ingredientflow = flow.add {
534 style = "auto_research_tech_flow",
536 direction = "horizontal"
539 local sprite = "auto_research_item_" .. ingredientname
540 if not helpers.is_valid_sprite_path(sprite) then
541 sprite = "auto_research_unknown"
543 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}
544 counter = counter + 1
548 updateTechnologyList = function(scrollpane, technologies, player, show_queue_buttons)
549 if scrollpane.flow then
550 scrollpane.flow.destroy()
552 local flow = scrollpane.add{
554 style = "auto_research_list_flow",
556 direction = "vertical"
558 if #technologies > 0 then
559 for _, techname in pairs(technologies) do
560 local tech = player.force.technologies[techname]
562 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
563 if show_queue_buttons then
564 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. techname, sprite = "auto_research_prioritize_top"}
565 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. techname, sprite = "auto_research_prioritize_bottom"}
567 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_delete-" .. techname, sprite = "auto_research_delete"}
568 entryflow.add{type = "label", style = "auto_research_tech_label", caption = tech.localised_name}
569 for _, ingredient in pairs(tech.research_unit_ingredients) do
570 local sprite = "auto_research_item_" .. ingredient.name
571 if not helpers.is_valid_sprite_path(sprite) then
572 sprite = "auto_research_unknown"
574 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
579 local entryflow = flow.add{type = "flow", direction = "horizontal"}
580 entryflow.add{type = "label", caption = {"auto_research_gui.none"}}
584 updateSearchResult = function(player, text)
585 local scrollpane = player.gui.top.auto_research_gui.flow.search
586 if scrollpane.flow then
587 scrollpane.flow.destroy()
589 local flow = scrollpane.add{
591 style = "auto_research_list_flow",
593 direction = "vertical"
595 local ingredients_filter = player.gui.top.auto_research_gui.flow.searchflow.auto_research_ingredients_filter_search_results.state
596 local config = getConfig(player.force)
598 text = string.lower(text)
599 -- NOTICE: localised name matching does not work at present, pending unlikely changes to Factorio API
601 for name, tech in pairs(player.force.technologies) do
602 table.insert(techs, {name, tech})
604 sortTechsByEffort(techs, config)
605 for _ , namedTech in pairs(techs) do
606 local name = namedTech[1]
607 local tech = namedTech[2]
608 if not tech.researched and tech.enabled and #tech.research_unit_ingredients > 0 then
609 local showtech = false
610 if string.find(string.lower(name), text, 1, true) then
611 -- show techs that match by name
613 -- elseif string.find(string.lower(game.technology_prototypes[name].localised_name), text, 1, true) then
614 -- -- show techs that match by localised name
617 for _, effect in pairs(tech.prototype.effects) do
618 if string.find(effect.type, text, 1, true) then
619 -- show techs that match by effect type
621 elseif effect.type == "unlock-recipe" then
622 if string.find(effect.recipe, text, 1, true) then
623 -- show techs that match by unlocked recipe name
625 -- elseif string.find(string.lower(game.recipe_prototypes[effect.recipe].localised_name), text, 1, true) then
626 -- -- show techs that match by unlocked recipe localised name
629 for _, product in pairs(prototypes.recipe[effect.recipe].products) do
630 if string.find(product.name, text, 1, true) then
631 -- show techs that match by unlocked recipe product name
633 -- elseif string.find(string.lower(game.item_prototypes[product.name].localised_name), text, 1, true) then
634 -- -- show techs that match by unlocked recipe product localised name
637 local prototype = prototypes.item[product.name]
639 if prototype.place_result then
640 if string.find(prototype.place_result.name, text, 1, true) then
641 -- show techs that match by unlocked recipe product placed entity name
643 -- elseif string.find(string.lower(game.entity_prototypes[prototype.place_result.name].localised_name), text, 1, true) then
644 -- -- show techs that match by unlocked recipe product placed entity localised name
647 elseif prototype.place_as_equipment_result then
648 if string.find(prototype.place_as_equipment_result.name, text, 1, true) then
649 -- show techs that match by unlocked recipe product placed equipment name
651 -- elseif string.find(string.lower(game.equipment_prototypes[prototype.place_as_equipment_result.name].localised_name), text, 1, true) then
652 -- -- show techs that match by unlocked recipe product placed equipment localised name
655 elseif prototype.place_as_tile_result then
656 if string.find(prototype.place_as_tile_result.result.name, text, 1, true) then
657 -- show techs that match by unlocked recipe product placed tile name
659 -- elseif string.find(string.lower(prototype.place_as_tile_result.result.localised_name), text, 1, true) then
660 -- -- show techs that match by unlocked recipe product placed tile localised name
671 if showtech and config.prioritized_techs then
672 for _, queued_tech in pairs(config.prioritized_techs) do
673 if name == queued_tech then
679 if showtech and config.deprioritized_techs then
680 for _, blacklisted_tech in pairs(config.deprioritized_techs) do
681 if name == blacklisted_tech then
687 if showtech and ingredients_filter then
688 for _, ingredient in pairs(tech.research_unit_ingredients) do
689 if not config.allowed_ingredients[ingredient.name] then
690 -- filter out techs that require disallowed ingredients (optional)
697 local entryflow = flow.add{type = "flow", style = "auto_research_tech_flow", direction = "horizontal"}
698 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_top-" .. name, sprite = "auto_research_prioritize_top"}
699 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_queue_bottom-" .. name, sprite = "auto_research_prioritize_bottom"}
700 entryflow.add{type = "sprite-button", style = "auto_research_sprite_button", name = "auto_research_blacklist-" .. name, sprite = "auto_research_deprioritize"}
701 entryflow.add{type = "label", style = "auto_research_tech_label", name = name, caption = tech.localised_name}
702 for _, ingredient in pairs(tech.research_unit_ingredients) do
703 local sprite = "auto_research_item_" .. ingredient.name
704 if not helpers.is_valid_sprite_path(sprite) then
705 sprite = "auto_research_unknown"
707 entryflow.add{type = "sprite", style = "auto_research_sprite", sprite = sprite}
716 script.on_configuration_changed(function()
717 for _, force in pairs(game.forces) do
718 getConfig(force, true) -- triggers initialization of force config
721 script.on_event(defines.events.on_player_created, function(event)
722 local force = game.players[event.player_index].force
723 local config = getConfig(force) -- triggers initialization of force config
724 -- set any default queued/blacklisted techs
725 local queued_tech = settings.get_player_settings(game.players[event.player_index])["queued-tech-setting"].value
726 for tech in string.gmatch(queued_tech, "[^,$]+") do
727 tech = string.gsub(tech, "%s+", "")
728 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
729 table.insert(config.prioritized_techs, tech)
732 local blacklisted_tech = settings.get_player_settings(game.players[event.player_index])["blacklisted-tech-setting"].value
733 for tech in string.gmatch(blacklisted_tech, "[^,$]+") do
734 tech = string.gsub(tech, "%s+", "")
735 if force.technologies[tech] and force.technologies[tech].enabled and not force.technologies[tech].researched then
736 table.insert(config.deprioritized_techs, tech)
739 startNextResearch(force, true)
741 script.on_event(defines.events.on_force_created, function(event)
742 getConfig(event.force) -- triggers initialization of force config
744 script.on_event(defines.events.on_research_finished, onResearchFinished)
745 script.on_event(defines.events.on_gui_checked_state_changed, gui.onCheckboxClick)
746 script.on_event(defines.events.on_gui_click, gui.onClick)
747 script.on_event(defines.events.on_gui_text_changed, function(event)
748 if event.element.name ~= "auto_research_search_text" then
751 gui.updateSearchResult(game.players[event.player_index], event.element.text)
755 script.on_event("auto_research_toggle", function(event)
756 local player = game.players[event.player_index]
757 gui.toggleGui(player)
760 -- Add remote interfaces for enabling/disabling Auto Research
761 remote.add_interface("auto_research", {
762 enabled = function(forcename, value) setAutoResearch(game.forces[forcename], value) end,
763 queued_only = function(forcename, value) setQueuedOnly(game.forces[forcename], value) end,
764 allow_switching = function(forcename, value) setAllowSwitching(game.forces[forcename], value) end,
765 announce_completed = function(forcename, value) setAnnounceCompletedResearch(game.forces[forcename], value) end,
766 deprioritize_infinite_tech = function(forcename, value) setDeprioritizeInfiniteTech(game.forces[forcename], value) end