├── .gitignore ├── README.md ├── changelog.txt ├── constants.lua ├── control.lua ├── data.lua ├── graphics ├── frame-action-icons.png ├── gui2.png ├── rip.png ├── shortcut-disabled.png └── shortcut.png ├── info.json ├── locale └── en │ └── en.cfg ├── migrations └── 2020_12_15_AutoTrash_5.3.9.lua ├── prototypes └── styles.lua ├── scripts ├── global-data.lua ├── gui-util.lua ├── gui.lua ├── migrations.lua ├── player-data.lua ├── presets.lua ├── spidertron.lua └── util.lua ├── settings.lua └── thumbnail.png /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | build/* 3 | factorio_mods 4 | /.buildpath 5 | /.project 6 | *.odg 7 | *.xcf 8 | Instructions.png 9 | Instructions2.png 10 | cookiejar.txt 11 | *.zip -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Configure different presets for your logistic request and auto-trash slots 2 | 3 | ![Config](https://mods-data.factorio.com/assets/ee3e3a9131dcaddd3c9ee49a3dced17af37a6de8.png) 4 | 5 | Features: 6 | --- 7 | - Configure request and trash settings in one window 8 | - Save and load multiple presets 9 | - Export/Import the configuration and presets (as a string or blueprint/book) 10 | - Display the requests status: 11 | + Red: missing items 12 | + Yellow: items are on the way 13 | + Blue: some items are on the way, but not enough are available 14 | - Pause requests when dying 15 | - Load one or more preset after respawning 16 | - Trash unrequested items 17 | - Pause autotrash when not in certain networks 18 | - Pause requests/auto-trash individually 19 | - Shift click configured items in the gui to quickly reorder them (keep shift pressed when clicking the second time): 20 | ![ClickDrop](https://i.imgur.com/h8XcENe.gif) 21 | 22 | Notes: 23 | --- 24 | - This mod may change your vanilla Logistic and Auto Trash slots at any time (depending on your settings), so i suggest to configure them only in the mods gui if you don't want to loose your changes 25 | - Export/Import: I suggest to keep the created strings as a blueprint in the blueprint library. If you have modded items configured and import the string in a save without these items you might even loose items that are still available. Importing from a blueprint from the library will only remove the missing items, keeping everything else intact. 26 | 27 | Hotkeys: 28 | --- 29 | - Shift + P: Pauses Autotrash 30 | - Shift + O: Pause logistic requests 31 | - Shift + T: Add item on cursor to temporary trash. Pause/Unpause Autotrash if cursor is empty 32 | - Control + L: Toggle AutoTrash gui 33 | - Unbound: Toggle trashing of unrequested items 34 | 35 | Commands: 36 | --- 37 | - /at_import - Import the vanilla request and trash settings into the mod gui: 38 | - /at_reset - Reset gui 39 | - /at_compress - Removes empty rows in the logistics configuratuon gui 40 | - /at_insert_row - Add an empty row after row #, e.g. /at_insert_row 2 - Inserts an empty row after row #2 41 | 42 | Todo: 43 | --- 44 | - Temporary requests 45 | - Order blueprint items 46 | 47 | [More info](https://forums.factorio.com/viewtopic.php?f=97&t=16016) -------------------------------------------------------------------------------- /changelog.txt: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------------------------- 2 | Version: 5.3.16 3 | Date: ???? 4 | Changes: 5 | --------------------------------------------------------------------------------------------------- 6 | Version: 5.3.15 7 | Date: 2024-10-09 8 | Bugfixes: 9 | - Fixed crash when ipmorting a preset 10 | --------------------------------------------------------------------------------------------------- 11 | Version: 5.3.14 12 | Date: 2024-10-05 13 | Bugfixes: 14 | - Fixed crash when saving a preset on a spidertron while in editor mode 15 | - Possibly fixed crash when importing a preset via blueprint 16 | - Possibly fixed crash when items stored in presets get removed from the game 17 | Changes: 18 | - "Set trash to requests" quick action now works when 0 items are requested 19 | --------------------------------------------------------------------------------------------------- 20 | Version: 5.3.13 21 | Date: 2021-02-03 22 | Bugfixes: 23 | - Fixed crash when mod settings are changed by script 24 | --------------------------------------------------------------------------------------------------- 25 | Version: 5.3.12 26 | Date: 2021-01-15 27 | Bugfixes: 28 | - Fixed crash when changing request/trash amounts with an empty slot selected 29 | --------------------------------------------------------------------------------------------------- 30 | Version: 5.3.11 31 | Date: 2021-01-08 32 | Changes: 33 | - Added a quick action to import the config from the logistics tab 34 | --------------------------------------------------------------------------------------------------- 35 | Version: 5.3.10 36 | Date: 2020-12-21 37 | Features: 38 | - Added a quick action to import the config from the inventory 39 | - Shift + Ctrl clicking presets will append presets without filling gaps 40 | --------------------------------------------------------------------------------------------------- 41 | Version: 5.3.9 42 | Date: 2020-12-15 43 | Bugfixes: 44 | - Fixed crash when starting a game from custom scenario that used an older version of the mod 45 | --------------------------------------------------------------------------------------------------- 46 | Version: 5.3.8 47 | Date: 2020-12-09 48 | Changes: 49 | - Added two new commands: 50 | /at_compress - Removes empty rows in the logistics configuratuon gui 51 | /at_insert_row - Add an empty row after row #, e.g. /at_insert_row 2 - Inserts an empty row after row #2 52 | For these commands a row is always 10 slots, regardless of the "Columns to display" mod setting 53 | - Removed blue as a status indicator: Yellow button background means items are being delivered, red means no items are available/being delivered 54 | - Minor changes to scrollbars to prevent the width of the gui changing 55 | Bugfixes: 56 | - Fixed crash when removing a preset via Spidertron and resaving it with the same name 57 | --------------------------------------------------------------------------------------------------- 58 | Version: 5.3.7 59 | Date: 2020-12-06 60 | Bugfixes: 61 | - Fixed crash with invalid status display 62 | --------------------------------------------------------------------------------------------------- 63 | Version: 5.3.6 64 | Date: 2020-12-02 65 | Bugfixes: 66 | - Fixed crash when importing presets via blueprint string 67 | --------------------------------------------------------------------------------------------------- 68 | Version: 5.3.5 69 | Date: 2020-11-30 70 | Changes: 71 | - Added a "Trash unrequested" button to the spidertron gui: 72 | It will set every unrequested item to a maximum amount of 0 in the spidertrons logistics tab. 73 | - If requested amount and maximum amount are the same, only one number is shown in the gui 74 | - Increased performance when dealing with a lot of configured items 75 | - The gui for the spidertron is only shown for spidertrons with a logistics tab 76 | - Added a delete button to the spidertron gui. Note that this will also delete the preset in the characters gui. 77 | Bugfixes: 78 | - Fixed crash when trying to save a preset via the spidertron and the character gui was invalid 79 | - Fixed sprites for the pin and reset button 80 | --------------------------------------------------------------------------------------------------- 81 | Version: 5.3.4 82 | Date: 2020-11-29 83 | Changes: 84 | - Fixed error when trying to export a preset with no items set 85 | --------------------------------------------------------------------------------------------------- 86 | Version: 5.3.3 87 | Date: 2020-11-28 88 | Changes: 89 | - Changed the shortcut icon to red and yellow arrows 90 | - Added a hotkey to toggle trashing unrequested items 91 | - Added a checkbox to turn off trashing unrequested items once the inventory is cleaned up 92 | Bugfixes: 93 | - Fixed error when saving a preset via the spidertron gui 94 | --------------------------------------------------------------------------------------------------- 95 | Version: 5.3.2 96 | Date: 2020-11-26 97 | Features: 98 | - Added the ability to load presets into the spidertron: 99 | - Presets are the same as the players presets 100 | - You can save the spidertrons current logistic requests as a preset 101 | - Holding shift when clicking on a preset adds it to the current requests. This allows combining multiple presets 102 | --------------------------------------------------------------------------------------------------- 103 | Version: 5.3.1 104 | Date: 2020-11-24 105 | Changes: 106 | - Removed the (+/-) buttons, a new row will be added if something is put in the last row 107 | Bugfixes: 108 | - Fixed error when adding AutoTrash to an existing save 109 | --------------------------------------------------------------------------------------------------- 110 | Version: 5.3.0 111 | Date: 2020-11-23 112 | Changes: 113 | - Updated for Factorio 1.1 114 | --------------------------------------------------------------------------------------------------- 115 | Version: 5.2.17 116 | Date: 2020-12-21 117 | Features: 118 | - Added a quick action to import the config from the inventory 119 | - Shift + Ctrl clicking presets will append presets without filling gaps 120 | --------------------------------------------------------------------------------------------------- 121 | Version: 5.2.16 122 | Date: 2020-11-21 123 | Changes: 124 | - Added shortcut to toggle the AutoTrash gui 125 | - Added mod setting to hide the button in the top left 126 | - Removed commands /at_show and /at_hide 127 | --------------------------------------------------------------------------------------------------- 128 | Version: 5.2.15 129 | Date: 2020-11-20 130 | Bugfixes: 131 | - Fixed gui buttons becoming unresponsive. 132 | --------------------------------------------------------------------------------------------------- 133 | Version: 5.2.14 134 | Date: 2020-11-18 135 | Changes: 136 | - Sliders change the amount by one stack at a time. 137 | - Added support for multiple main networks. 138 | --------------------------------------------------------------------------------------------------- 139 | Version: 5.2.13 140 | Date: 2020-11-08 141 | Bugfixes: 142 | - Fixed error when shift-clicking items to move them. 143 | --------------------------------------------------------------------------------------------------- 144 | Version: 5.2.12 145 | Date: 2020-11-07 146 | Changes: 147 | - Applying changes or pressing enter after changing the trash amount will display a message if the the amount got adjusted to the request amount. 148 | Bugfixes: 149 | - Fixed error when changing the trash amount via keyboard. 150 | --------------------------------------------------------------------------------------------------- 151 | Version: 5.2.11 152 | Date: 2020-11-06 153 | Bugfixes: 154 | - Fixed "Trash above requested" not working if no trash amount was set. 155 | - Fixed temporary trash not working if the item was already set. 156 | - Fixed errors when using the commands: /at_show and /at_hide. 157 | --------------------------------------------------------------------------------------------------- 158 | Version: 5.2.10 159 | Date: 2020-11-01 160 | Bugfixes: 161 | - Fixed requests not being set properly. 162 | - Fixed button and textfields showing nothing if request is 0 and trash is unlimited. 163 | --------------------------------------------------------------------------------------------------- 164 | Version: 5.2.9 165 | Date: 2020-10-31 166 | Changes: 167 | - Added a pin button to the gui, to keep it open when pressing E/Escape. By default the gui will stay open. 168 | - Added a tooltip to the top button, showing the control to toggle the status display. 169 | - Researching the personal logistics opens the status display. 170 | Bugfixes: 171 | - Fixed UPS drop when deconstructing roboports with construction robots. 172 | --------------------------------------------------------------------------------------------------- 173 | Version: 5.2.8 174 | Date: 2020-10-30 175 | Bugfixes: 176 | - Fixed crash when requesting 0 of an item and infinite trash amount while "Trash above requested" is active. 177 | --------------------------------------------------------------------------------------------------- 178 | Version: 5.2.7 179 | Date: 2020-10-30 180 | Changes: 181 | - Made displayed columns and rows adjustable again. Columns are restricted from 5 to 40 in increments of 5. 182 | --------------------------------------------------------------------------------------------------- 183 | Version: 5.2.6 184 | Date: 2020-10-27 185 | Bugfixes: 186 | - Fixed error after players temporary trash slots have been emptied. 187 | --------------------------------------------------------------------------------------------------- 188 | Version: 5.2.5 189 | Date: 2020-10-27 190 | Bugfixes: 191 | - Fixed Unknown sprite error when ModuleInserter mod is missing. 192 | --------------------------------------------------------------------------------------------------- 193 | Version: 5.2.4 194 | Date: 2020-10-27 195 | Changes: 196 | - Updated the gui to look more like the vanilla gui. 197 | - Removed mod settings to adjust the number of displayed rows/columns. 198 | - Added flib as a dependency. 199 | --------------------------------------------------------------------------------------------------- 200 | Version: 5.2.2 201 | Date: 2020-09-20 202 | Changes: 203 | - Changed AutoTrash to be enabled as soon as the vanilla logistics tab is available. 204 | --------------------------------------------------------------------------------------------------- 205 | Version: 5.2.1 206 | Date: 2020-09-20 207 | Features: 208 | - Added buttons to export/import all presets at once. 209 | Bugfixes: 210 | - Fixed importing would mess up the item order in the gui. 211 | --------------------------------------------------------------------------------------------------- 212 | Version: 5.2.0 213 | Date: 28. 07. 2020 214 | Changes: 215 | - Changed the GUI to be draggable. 216 | - Changed default number of columns to 10, to match the characters logistics GUI. 217 | - Added setting to set the trash amount to the requested amount for new items. 218 | - Removed the "Pause requests on death" setting, since that is now a vanilla feature. 219 | - Reenable logistic requests after respawning when a death preset is selected. 220 | Bugfixes: 221 | - Fixed crash with the Rocket Rush scenario. 222 | - Fixed error when exporting an empty configuration. 223 | --------------------------------------------------------------------------------------------------- 224 | Version: 5.1.4 225 | Date: 26. 05. 2020 226 | Bugfixes: 227 | - Fixed startup errors with Factorio 0.18.27. 228 | --------------------------------------------------------------------------------------------------- 229 | Version: 5.1.3 230 | Date: 04. 05. 2020 231 | Bugfixes: 232 | - Fixed blueprints, books and planners being autotrashed. 233 | --------------------------------------------------------------------------------------------------- 234 | Version: 5.1.2 235 | Date: 15. 04. 2020 236 | Bugfixes: 237 | - Maybe fixed a crash when updating from a save with now invalid items. 238 | --------------------------------------------------------------------------------------------------- 239 | Version: 5.1.1 240 | Date: 20. 03. 2020 241 | Bugfixes: 242 | - Fixed trashing unrequested items would set all requests to zero. 243 | --------------------------------------------------------------------------------------------------- 244 | Version: 5.1.0 245 | Date: 19. 03. 2020 246 | Changes: 247 | - Updated to work with the new character GUI added in 0.18.13. 248 | - Removed obsolete setting to lock infinite slots behind a research. 249 | - Updated gui to the dark logistic slots. 250 | Bugfixes: 251 | - Fixed out of range error when AutoTrash was added to an existing save. 252 | --------------------------------------------------------------------------------------------------- 253 | Version: 5.0.2 254 | Date: 05. 03. 2020 255 | Bugfixes: 256 | - Fixed unknown sprite error. 257 | --------------------------------------------------------------------------------------------------- 258 | Version: 5.0.1 259 | Date: 31. 01. 2020 260 | Bugfixes: 261 | - Fixed error when trying to import an invalid blueprint/string. 262 | --------------------------------------------------------------------------------------------------- 263 | Version: 5.0.0 264 | Date: 26. 01. 2020 265 | Changes: 266 | - Updated for Factorio 0.18. 267 | --------------------------------------------------------------------------------------------------- 268 | Version: 4.2.0 269 | Date: 26. 01. 2020 270 | Changes: 271 | - Added mod setting to lock unlimited request slots behind the final character logistic slots research. 272 | --------------------------------------------------------------------------------------------------- 273 | Version: 4.1.11 274 | Date: 06. 10. 2019 275 | Bugfixes: 276 | - Fixed setting a main network wouldn't work at all when the player had ropobort equipment in the armor. 277 | --------------------------------------------------------------------------------------------------- 278 | Version: 4.1.10 279 | Date: 06. 10. 2019 280 | Bugfixes: 281 | - Fixed auto pausing when the main network became invalid. 282 | --------------------------------------------------------------------------------------------------- 283 | Version: 4.1.9 284 | Date: 20. 09. 2019 285 | Changes: 286 | - Item status display now shows the number of missing items to fulfill the request. 287 | Bugfixes: 288 | - Fixed request slot count not resetting properly when clearing requests. 289 | --------------------------------------------------------------------------------------------------- 290 | Version: 4.1.8 291 | Date: 18. 07. 2019 292 | Bugfixes: 293 | - Fixed error when loading a save that requires a migration to be run. 294 | --------------------------------------------------------------------------------------------------- 295 | Version: 4.1.7 296 | Date: 08. 07. 2019 297 | Changes: 298 | - Requests with 0 amount are no longer set in the vanilla gui. 299 | Bugfixes: 300 | - Fixed crash when importing a blueprint with requests. 301 | --------------------------------------------------------------------------------------------------- 302 | Version: 4.1.6 303 | Date: 03. 07. 2019 304 | Changes: 305 | - Adding AutoTrash to an existing save only opens the gui when requests/trash is set. 306 | Bugfixes: 307 | - Fixed items in armor, gun and ammo inventory not being counted for the request status display. 308 | - Fixed possible crash when loading a scenario in the map editor. 309 | --------------------------------------------------------------------------------------------------- 310 | Version: 4.1.5 311 | Date: 23. 06. 2019 312 | Changes: 313 | - Preset textfield only gets cleared when selecting multiple presets. 314 | - Select all text when clicking the preset textfield. 315 | - Adding AutoTrash to an existing save will import existing request/trash filters. 316 | --------------------------------------------------------------------------------------------------- 317 | Version: 4.1.4 318 | Date: 18. 06. 2019 319 | Bugfixes: 320 | - Fixed crash when entering a Factorissimo2 building. 321 | - Fixed error when creating a new scenario via map editor. 322 | --------------------------------------------------------------------------------------------------- 323 | Version: 4.1.3 324 | Date: 16. 06. 2019 325 | Changes: 326 | - Added command: /at_import : Imports the vanilla request and trash settings into the mod gui. 327 | Bugfixes: 328 | - Fixed error when updating players without a character. 329 | --------------------------------------------------------------------------------------------------- 330 | Version: 4.1.2 331 | Date: 16. 06. 2019 332 | Features: 333 | - Combined logistic requests and trash configuration into one window. 334 | - Click and drop: Hold shift when clicking a configured item, then shift click another button to swap the buttons. 335 | - It is now possible to load multiple presets at once. If more than one preset contains an item, the maximum request/trash amount is chosen. 336 | - Right click the main button to quickly load and apply a preset. 337 | - Shift + Right click the main button to display the status of your requests. 338 | - Different colors for indicating the status of an order: 339 | - grey: All items are delivered 340 | - red: Not enough items available in the network 341 | - yellow: Items are on the way 342 | - blue: Some items are on the way but the order can't be fulfilled 343 | - Added buttons to automatically load stored presets when respawning. 344 | - Added buttons to import/export the configuration. Holding shift when clicking the export button creates a blueprint with constant combinators containing the configuration. 345 | - Added multiple mod settings: 346 | - Pause requests after respawning 347 | - Reset configuration when closing the gui without applying the changes 348 | - Close gui when applying the changes 349 | - Overwrite presets 350 | - Adjust the number of rows/columns to be displayed 351 | --------------------------------------------------------------------------------------------------- 352 | Version: 4.0.6 353 | Date: 23. 05. 2019 354 | Bugfixes: 355 | - Fixed trashing not working at all. 356 | --------------------------------------------------------------------------------------------------- 357 | Version: 4.0.5 358 | Date: 01. 05. 2019 359 | Bugfixes: 360 | - updated for Factorio 0.17.35. 361 | --------------------------------------------------------------------------------------------------- 362 | Version: 4.0.4 363 | Date: 31. 03. 2019 364 | Bugfixes: 365 | - Fixed ruleset buttons not working in some circumstances. 366 | --------------------------------------------------------------------------------------------------- 367 | Version: 4.0.3 368 | Date: 30. 03. 2019 369 | Bugfixes: 370 | - Fixed items with equipment grid loosing all equipment when being trashed. 371 | --------------------------------------------------------------------------------------------------- 372 | Version: 4.0.2 373 | Date: 29. 03. 2019 374 | Bugfixes: 375 | - Fixed error when saving a logistic request with amount of 0. 376 | --------------------------------------------------------------------------------------------------- 377 | Version: 4.0.1 378 | Date: 10. 03. 2019 379 | Changes: 380 | - Updated for Factorio 0.17. 381 | - Replaced buttons in the UI with filter-like buttons (like in the vanilla logistics and autotrash windows). 382 | - To set a filter use left click, to reset use right click. 383 | --------------------------------------------------------------------------------------------------- 384 | Version: 3.0.3 385 | Date: 21. 04. 2018 386 | Bugfixes: 387 | - Fixed crash when mining a ropobort. 388 | --------------------------------------------------------------------------------------------------- 389 | Version: 3.0.2 390 | Date: 26. 03. 2018 391 | Bugfixes: 392 | - Fixed checkboxes not saving changes. 393 | --------------------------------------------------------------------------------------------------- 394 | Version: 3.0.1 395 | Date: 06. 01. 2018 396 | Bugfixes: 397 | - Fixed gui error. 398 | --------------------------------------------------------------------------------------------------- 399 | Version: 3.0.0 400 | Date: 14. 12. 2017 401 | Changes: 402 | - Updated for Factorio 0.16. -------------------------------------------------------------------------------- /constants.lua: -------------------------------------------------------------------------------- 1 | local constants = {} 2 | 3 | constants.max_request = 4294967295 4 | 5 | constants.quick_actions = { 6 | [1] = {"at-gui.quick-actions"}, 7 | [2] = {"at-gui.clear-requests"}, 8 | [3] = {"at-gui.clear-trash"}, 9 | [4] = {"at-gui.clear-both"}, 10 | [5] = {"at-gui.trash-to-requests"}, 11 | [6] = {"at-gui.requests-to-trash"}, 12 | [7] = {"at-gui.import-requests"}, 13 | [8] = {"at-gui.import-from-inventory"} 14 | } 15 | 16 | constants.trash_blacklist = { 17 | ["blueprint"] = true, 18 | ["blueprint-book"] = true, 19 | ["deconstruction-item"] = true, 20 | ["upgrade-item"] = true, 21 | ["copy-paste-tool"] = true, 22 | ["selection-tool"] = true, 23 | } 24 | 25 | constants.gui_dimensions = { 26 | window = 432, 27 | spidertron = 752, 28 | network_flow = 214 29 | } 30 | return constants -------------------------------------------------------------------------------- /control.lua: -------------------------------------------------------------------------------- 1 | local event = require("__flib__.event") 2 | local gui = require("__flib__.gui-beta") 3 | local migration = require("__flib__.migration") 4 | 5 | local constants = require("constants") 6 | local trash_blacklist = constants.trash_blacklist 7 | local global_data = require("scripts.global-data") 8 | local player_data = require("scripts.player-data") 9 | local migrations = require("scripts.migrations") 10 | local at_gui = require("scripts.gui") 11 | local spider_gui = require("scripts.spidertron") 12 | 13 | local at_util = require("scripts.util") 14 | local presets = require("scripts.presets") 15 | 16 | local set_requests = at_util.set_requests 17 | local pause_trash = at_util.pause_trash 18 | local unpause_trash = at_util.unpause_trash 19 | local get_network_entity = at_util.get_network_entity 20 | local in_network = at_util.in_network 21 | 22 | --TODO: "import" items from quickbars (automatically or by button?), add full rows and preserve quickbar layout 23 | --[[ 24 | - register on_player_main_inventory_changed etc conditionally 25 | - Map setting to disable updating button styles / status display? 26 | - Load different profiles depending on the network? 27 | ]]-- 28 | 29 | local function on_nth_tick() 30 | for _, p in pairs(game.connected_players) do 31 | if p.character then 32 | local pdata = global._pdata[p.index] 33 | if pdata.flags.gui_open then 34 | at_gui.update_button_styles(p, pdata) 35 | end 36 | if pdata.flags.status_display_open then 37 | at_gui.update_status_display(p, pdata) 38 | end 39 | end 40 | end 41 | end 42 | 43 | local function register_conditional_events() 44 | event.on_nth_tick(nil) 45 | event.on_nth_tick(settings.global["autotrash_update_rate"].value + 1, on_nth_tick) 46 | end 47 | 48 | -- BOOTSTRAP 49 | 50 | local function on_init() 51 | global_data.init() 52 | global_data.refresh() 53 | for _, force in pairs(game.forces) do 54 | if force.character_logistic_requests then 55 | global.unlocked_by_force[force.name] = true 56 | end 57 | for player_index, player in pairs(force.players) do 58 | local pdata = player_data.init(player_index) 59 | at_gui.init(player, pdata) 60 | if player.character and force.character_logistic_requests then 61 | if next(pdata.config_tmp.config) then 62 | pdata.presets["at_imported"] = at_util.copy_preset(pdata.config_tmp) 63 | pdata.selected_presets = {at_imported = true} 64 | end 65 | at_gui.create_main_window(player, pdata) 66 | at_gui.open(player, pdata) 67 | end 68 | end 69 | end 70 | register_conditional_events() 71 | end 72 | event.on_init(on_init) 73 | 74 | local function on_load() 75 | register_conditional_events() 76 | end 77 | event.on_load(on_load) 78 | 79 | local function on_configuration_changed(data) 80 | local removed 81 | if migration.on_config_changed(data, migrations) then 82 | at_util.remove_invalid_items() 83 | global_data.refresh() 84 | removed = true 85 | for index, pdata in pairs(global._pdata) do 86 | local player = game.get_player(index) 87 | player_data.refresh(player, pdata) 88 | at_gui.recreate(player, pdata) 89 | end 90 | end 91 | if not removed then 92 | at_util.remove_invalid_items() 93 | end 94 | register_conditional_events() 95 | end 96 | event.on_configuration_changed(on_configuration_changed) 97 | 98 | -- GUI 99 | 100 | gui.hook_events(function(e) 101 | local player = game.get_player(e.player_index) 102 | local pdata = global._pdata[e.player_index] 103 | e.player = player 104 | e.pdata = pdata 105 | local msg = gui.read_action(e) 106 | if msg then 107 | local handler = at_gui.handlers[msg.gui] and at_gui.handlers[msg.gui][msg.action] 108 | if handler then 109 | handler(e, msg) 110 | elseif msg.gui == "spider" then 111 | handler = spider_gui.handlers[msg.action] 112 | if handler then 113 | --check for valid character gui here, because spider_gui may want to update the character gui 114 | if not (pdata.gui.main.window and pdata.gui.main.window.valid) then 115 | at_gui.recreate(player, pdata, true) 116 | end 117 | if (player.opened_gui_type == defines.gui_type.entity and player.opened and player.opened.type == "spider-vehicle") then 118 | e.entity = player.opened 119 | handler(e, msg) 120 | end 121 | end 122 | end 123 | elseif e.name == defines.events.on_gui_opened and e.gui_type == defines.gui_type.entity and e.entity.type == "spider-vehicle" then 124 | local spider_ref = pdata.gui.spider and pdata.gui.spider.main 125 | if not (spider_ref and spider_ref.valid) then 126 | spider_gui.init(player, pdata) 127 | end 128 | if not (pdata.gui.main.window and pdata.gui.main.window.valid) then 129 | at_gui.recreate(player, pdata, true) 130 | end 131 | if __DebugAdapter then 132 | spider_gui.init(player, pdata) 133 | end 134 | local hide = not e.entity.get_logistic_point(defines.logistic_member_index.character_requester) 135 | spider_gui.update(player, pdata, hide) 136 | end 137 | end) 138 | 139 | --that's a bad event to handle unrequested, since adding stuff to the trash filters immediately triggers the next on_main_inventory_changed event 140 | -- on_nth_tick might work better or only registering when some player has trash_unrequested set to true 141 | local function on_player_main_inventory_changed(e) 142 | local player = game.get_player(e.player_index) 143 | if not (player.character) then return end 144 | local pdata = global._pdata[e.player_index] 145 | local flags = pdata.flags 146 | if flags.has_temporary_requests then 147 | player_data.check_temporary_requests(player, pdata) 148 | end 149 | if not flags.pause_trash and flags.trash_unrequested then 150 | if set_requests(player, pdata) then 151 | at_gui.update_options(pdata) 152 | end 153 | end 154 | end 155 | event.on_player_main_inventory_changed(on_player_main_inventory_changed) 156 | 157 | -- Set trash to 0 if the item isn't set and set it to request if it is 158 | local function add_to_trash(player, item) 159 | if trash_blacklist[item] then 160 | player.print({"", at_util.item_prototype(item).localised_name, " is on the blacklist for trashing"}) 161 | return 162 | end 163 | local request = player_data.find_request(player, item) 164 | if request then 165 | request.max = request.min 166 | else 167 | request = {name = item, min = 0, max = 0} 168 | end 169 | if player_data.set_request(player, global._pdata[player.index], request, true) then 170 | player.print({"at-message.added-to-temporary-trash", at_util.item_prototype(item).localised_name}) 171 | end 172 | end 173 | 174 | 175 | 176 | event.on_player_selected_area(function(e) 177 | if e.item ~= "autotrash-network-selection" then return end 178 | local player = game.get_player(e.player_index) 179 | local pdata = global._pdata[e.player_index] 180 | for _, roboport in pairs(e.entities) do 181 | local robo_id = roboport.unit_number 182 | if not pdata.networks[robo_id] then 183 | local network = roboport.logistic_network 184 | for id, main_net in pairs(pdata.networks) do 185 | if main_net and main_net.valid and main_net.logistic_network == network then 186 | player.print{"at-message.network-exists", id} 187 | goto continue 188 | end 189 | end 190 | pdata.networks[robo_id] = roboport 191 | player.print{"at-message.added-network", robo_id} 192 | else 193 | player.print{"at-message.network-exists", robo_id} 194 | end 195 | ::continue:: 196 | end 197 | at_gui.update_networks(pdata) 198 | end) 199 | 200 | event.on_player_alt_selected_area(function(e) 201 | if e.item ~= "autotrash-network-selection" then return end 202 | local player = game.get_player(e.player_index) 203 | local pdata = global._pdata[e.player_index] 204 | for _, roboport in pairs(e.entities) do 205 | if pdata.networks[roboport.unit_number] then 206 | pdata.networks[roboport.unit_number] = nil 207 | player.print{"at-message.removed-network", roboport.unit_number} 208 | else 209 | local network = roboport.logistic_network 210 | for id, main_net in pairs(pdata.networks) do 211 | if main_net and main_net.valid and main_net.logistic_network == network then 212 | pdata.networks[id] = nil 213 | player.print{"at-message.removed-network", id} 214 | goto continue 215 | end 216 | end 217 | end 218 | ::continue:: 219 | end 220 | at_gui.update_networks(pdata) 221 | end) 222 | 223 | -- PLAYER 224 | 225 | event.on_player_created(function(e) 226 | local player = game.get_player(e.player_index) 227 | player_data.init(e.player_index) 228 | at_gui.init(player, global._pdata[e.player_index]) 229 | end) 230 | 231 | --TODO is this still needed? 232 | event.on_cutscene_cancelled(function(e) 233 | local player = game.get_player(e.player_index) 234 | local pdata = global._pdata[e.player_index] 235 | if not pdata then 236 | pdata = player_data.init(e.player_index) 237 | else 238 | player_data.refresh(player, pdata) 239 | end 240 | at_gui.init(player, pdata) 241 | end) 242 | 243 | event.on_player_removed(function(e) 244 | global._pdata[e.player_index] = nil 245 | register_conditional_events() 246 | end) 247 | 248 | event.on_player_toggled_map_editor(function(e) 249 | local player = game.get_player(e.player_index) 250 | local pdata = global._pdata[e.player_index] 251 | if pdata.flags.gui_open and not player.character then 252 | player.print{"at-message.no-character"} 253 | at_gui.close(player, pdata, true) 254 | end 255 | end) 256 | 257 | --TODO Display paused icons/checkboxes without clearing the requests? 258 | -- Vanilla now pauses logistic requests and trash when dying 259 | event.on_player_respawned(function(e) 260 | local player = game.get_player(e.player_index) 261 | if not player.character then return end 262 | local pdata = global._pdata[e.player_index] 263 | local selected_presets = pdata.death_presets 264 | if table_size(selected_presets) > 0 then 265 | local tmp = {config = {}, by_name = {}, max_slot = 0, c_requests = 0} 266 | for key, _ in pairs(selected_presets) do 267 | presets.merge(tmp, pdata.presets[key]) 268 | end 269 | at_gui.close(player, pdata) 270 | pdata.config_tmp = tmp 271 | pdata.config_new = at_util.copy_preset(tmp) 272 | 273 | set_requests(player, pdata) 274 | player.character_personal_logistic_requests_enabled = true 275 | at_gui.update_status_display(player, pdata) 276 | end 277 | end) 278 | 279 | event.on_player_changed_position(function(e) 280 | local player = game.get_player(e.player_index) 281 | if not player.character then return end 282 | local pdata = global._pdata[e.player_index] 283 | --Rocket rush scenario might teleport before AutoTrash gets a chance to init?! 284 | if not pdata then 285 | pdata = player_data.init(e.player_index) 286 | end 287 | local current = (pdata.current_network and pdata.current_network.valid) and pdata.current_network 288 | local current_net = current and current.logistic_network 289 | local maybe_new = get_network_entity(player) 290 | local maybe_new_net = maybe_new and maybe_new.logistic_network 291 | if maybe_new_net ~= current_net then 292 | if pdata.flags.gui_open then 293 | at_gui.update_button_styles(player, pdata) 294 | end 295 | pdata.current_network = maybe_new 296 | end 297 | if not pdata.flags.trash_network then 298 | return 299 | end 300 | local is_in_network = in_network(player, pdata) 301 | local paused = pdata.flags.pause_trash 302 | if not is_in_network and not paused then 303 | pause_trash(player, pdata) 304 | at_gui.update_main_button(player, pdata) 305 | if pdata.settings.display_messages then 306 | player.print({"at-message.trash-paused"}) 307 | end 308 | return 309 | elseif is_in_network and paused then 310 | unpause_trash(player, pdata) 311 | at_gui.update_main_button(player, pdata) 312 | if pdata.settings.display_messages then 313 | player.print({"at-message.trash-unpaused"}) 314 | end 315 | end 316 | end) 317 | 318 | local function on_pre_mined_item(e) 319 | local entity = e.entity 320 | if not (entity.logistic_network and entity.logistic_network.valid) then return end 321 | local cells 322 | for pi, pdata in pairs(global._pdata) do 323 | local player = game.get_player(pi) 324 | local main = pdata.networks[entity.unit_number] 325 | local current = pdata.current_network 326 | if main and main.valid then 327 | cells = cells or entity.logistic_network.cells 328 | local found 329 | for _, cell in pairs(cells) do 330 | local owner = cell.owner 331 | if owner.valid and not owner.to_be_deconstructed() and owner ~= entity then 332 | pdata.networks[owner.unit_number] = owner 333 | found = true 334 | break 335 | end 336 | end 337 | pdata.networks[entity.unit_number] = nil 338 | if not found then 339 | player.print{"at-message.network-unset", entity.unit_number} 340 | end 341 | end 342 | if current and current.valid and entity == current then 343 | pdata.current_network = false 344 | cells = cells or entity.logistic_network.cells 345 | for _, cell in pairs(cells) do 346 | local owner = cell.owner 347 | if owner.valid and not owner.to_be_deconstructed() and owner ~= entity then 348 | pdata.current_network = owner 349 | break 350 | end 351 | end 352 | end 353 | at_gui.update_networks(pdata) 354 | end 355 | end 356 | local robofilter = {{filter = "type", type = "roboport"}} 357 | event.on_player_mined_entity(on_pre_mined_item, robofilter) 358 | event.on_robot_mined_entity(on_pre_mined_item, robofilter) 359 | event.on_entity_died(on_pre_mined_item, robofilter) 360 | event.script_raised_destroy(on_pre_mined_item, robofilter) 361 | 362 | local function on_built_entity(e) 363 | local entity = e.entity or e.created_entity 364 | local network = entity.logistic_network 365 | local exists 366 | if not (network and network.valid) then return end 367 | for pi, pdata in pairs(global._pdata) do 368 | local player = game.get_player(pi) 369 | for id, roboport in pairs(pdata.networks) do 370 | if roboport and roboport.valid and network == roboport.logistic_network then 371 | if not exists then 372 | exists = id 373 | end 374 | if exists and exists ~= id then 375 | pdata.networks[id] = nil 376 | player.print{"at-message.merged-networks", exists, id} 377 | end 378 | end 379 | end 380 | at_gui.update_networks(pdata) 381 | exists = nil 382 | end 383 | end 384 | event.on_built_entity(on_built_entity, robofilter) 385 | event.on_robot_built_entity(on_built_entity, robofilter) 386 | event.script_raised_built(on_built_entity, robofilter) 387 | event.script_raised_revive(on_built_entity, robofilter) 388 | 389 | 390 | local function toggle_autotrash_pause(e, player) 391 | player = player or game.get_player(e.player_index) 392 | if not player.character then return end 393 | local pdata = global._pdata[player.index] 394 | player_data.import_when_empty(player, pdata) 395 | if pdata.flags.pause_trash then 396 | unpause_trash(player, pdata) 397 | else 398 | pause_trash(player, pdata) 399 | end 400 | at_gui.update_main_button(player, pdata) 401 | at_gui.close(player, pdata) 402 | end 403 | 404 | -- CUSTOM INPUT, SHORTCUT 405 | 406 | event.register("autotrash_pause", toggle_autotrash_pause) 407 | 408 | event.register("autotrash_pause_requests", function(e) 409 | local player = game.get_player(e.player_index) 410 | if not player.character then return end 411 | local pdata = global._pdata[player.index] 412 | player_data.import_when_empty(player, pdata) 413 | if pdata.flags.pause_requests then 414 | at_util.unpause_requests(player, pdata) 415 | else 416 | at_util.pause_requests(player, pdata) 417 | end 418 | at_gui.update_status_display(player, pdata) 419 | at_gui.update_main_button(player, pdata) 420 | at_gui.close(player, pdata) 421 | end) 422 | 423 | event.register({"autotrash-toggle-gui", defines.events.on_lua_shortcut}, function(e) 424 | if e.input_name or e.prototype_name == "autotrash-toggle-gui" then 425 | local pdata = global._pdata[e.player_index] 426 | if pdata.flags.can_open_gui then 427 | at_gui.toggle(game.get_player(e.player_index), pdata) 428 | end 429 | end 430 | end) 431 | 432 | event.register("autotrash-toggle-unrequested", function(e) 433 | local player = game.get_player(e.player_index) 434 | if not player.controller_type == defines.controllers.character then 435 | return 436 | end 437 | local pdata = global._pdata[e.player_index] 438 | pdata.flags.trash_unrequested = not pdata.flags.trash_unrequested 439 | at_gui.toggle_setting.trash_unrequested(player, pdata) 440 | at_gui.update_options(pdata) 441 | end) 442 | 443 | event.register("autotrash_trash_cursor", function(e) 444 | local player = game.get_player(e.player_index) 445 | if not player.character then 446 | player.print({"at-message.character-needed"}) 447 | return 448 | end 449 | if player.force.character_trash_slot_count > 0 then 450 | local cursorStack = player.cursor_stack 451 | if cursorStack.valid_for_read then 452 | add_to_trash(player, cursorStack.name) 453 | else 454 | toggle_autotrash_pause(e, player) 455 | end 456 | end 457 | end) 458 | 459 | local function on_runtime_mod_setting_changed(e) 460 | if e.setting == "autotrash_update_rate" then 461 | register_conditional_events() 462 | return 463 | end 464 | if not e.player_index then return end 465 | local player_index = e.player_index 466 | local player = game.get_player(player_index) 467 | local pdata = global._pdata[player_index] 468 | if not (player_index and pdata) then return end 469 | player_data.refresh(player, pdata) 470 | at_gui.update_main_button(player, pdata) 471 | if e.setting == "autotrash_gui_displayed_columns" then 472 | if pdata.gui.main.window and pdata.gui.main.window.valid then 473 | at_gui.adjust_size(pdata) 474 | else 475 | at_gui.recreate(player, pdata) 476 | end 477 | elseif e.setting == "autotrash_gui_rows_before_scroll" then 478 | if pdata.gui.main.window and pdata.gui.main.window.valid then 479 | local table_height = pdata.settings.rows * 40 480 | local gui_data = pdata.gui.main 481 | gui_data.slot_table.style.minimal_height = table_height 482 | gui_data.window.style.height = table_height + constants.gui_dimensions.window 483 | gui_data.window.force_auto_center() 484 | at_gui.adjust_slots(pdata) 485 | else 486 | at_gui.recreate(player, pdata) 487 | end 488 | elseif e.setting == "autotrash_status_count" or e.setting == "autotrash_status_columns" then 489 | at_gui.init_status_display(player, pdata, true) 490 | end 491 | end 492 | event.on_runtime_mod_setting_changed(on_runtime_mod_setting_changed) 493 | 494 | local function on_player_display_resolution_changed(e) 495 | local pdata = global._pdata[e.player_index] 496 | local player = game.get_player(e.player_index) 497 | player_data.refresh(player, pdata) 498 | if player.character then 499 | at_gui.recreate(player, pdata) 500 | else 501 | at_gui.close(player, pdata, true) 502 | end 503 | end 504 | event.on_player_display_resolution_changed(on_player_display_resolution_changed) 505 | event.on_player_display_scale_changed(on_player_display_resolution_changed) 506 | 507 | local function on_research_finished(e) 508 | local force = e.research.force 509 | if not global.unlocked_by_force[force.name] and force.character_logistic_requests then 510 | for _, player in pairs(force.players) do 511 | local pdata = global._pdata[player.index] 512 | if not pdata then 513 | pdata = player_data.init(player.index) 514 | end 515 | pdata.flags.can_open_gui = true 516 | player.set_shortcut_available("autotrash-toggle-gui", true) 517 | at_gui.update_main_button(player, pdata) 518 | if player.character then 519 | at_gui.create_main_window(player, pdata) 520 | at_gui.open_status_display(player, pdata) 521 | end 522 | end 523 | global.unlocked_by_force[force.name] = true 524 | end 525 | end 526 | event.on_research_finished(on_research_finished) 527 | 528 | local at_commands = { 529 | import = function(args) 530 | local player_index = args.player_index 531 | local player = game.get_player(player_index) 532 | local pdata = global._pdata[player_index] 533 | if not pdata then 534 | pdata = player_data.init(player_index) 535 | end 536 | if not player.character then return end 537 | pdata.config_tmp = player_data.combine_from_vanilla(player, pdata) 538 | at_gui.recreate(player, pdata) 539 | at_gui.open(player, pdata) 540 | at_gui.mark_dirty(player, pdata) 541 | end, 542 | 543 | reset = function(args) 544 | local player_index = args.player_index 545 | local player = game.get_player(player_index) 546 | local pdata = global._pdata[player_index] 547 | at_gui.destroy(player, pdata) 548 | at_gui.open(player, pdata) 549 | spider_gui.init(player, pdata) 550 | end, 551 | 552 | compress = function(args) 553 | local player_index = args.player_index 554 | local pdata = global._pdata[player_index] 555 | local config = pdata.config_tmp.config 556 | local decrease = 0 557 | local gap_size 558 | local columns = 10 559 | 560 | for i = 1, pdata.config_tmp.max_slot do 561 | if i % columns == 1 and not config[i] then 562 | --row starts empty, begin counting 563 | gap_size = 1 564 | elseif gap_size and not config[i] then 565 | gap_size = gap_size + 1 566 | else 567 | gap_size = nil 568 | if config[i] and decrease > 0 then 569 | if config[i-decrease] then 570 | if __DebugAdapter then 571 | __DebugAdapter.breakpoint() 572 | end 573 | end 574 | config[i-decrease] = config[i] 575 | config[i].slot = i - decrease 576 | config[i] = nil 577 | 578 | end 579 | end 580 | if gap_size == columns then 581 | decrease = decrease + columns 582 | gap_size = nil 583 | end 584 | end 585 | pdata.config_tmp.max_slot = pdata.config_tmp.max_slot - decrease 586 | at_gui.adjust_slots(pdata, 1) 587 | at_gui.update_buttons(pdata) 588 | end, 589 | 590 | insert_row = function(args) 591 | local player_index = args.player_index 592 | local player = game.get_player(player_index) 593 | local pdata = global._pdata[player_index] 594 | local row = tonumber(args.parameter) 595 | if not row then 596 | player.print(args.parameter .. " is not a number") 597 | return 598 | end 599 | pdata.selected = false 600 | local start = row * 10 + 1 601 | if start <= pdata.config_tmp.max_slot then 602 | local config = pdata.config_tmp.config 603 | for i = pdata.config_tmp.max_slot, start, -1 do 604 | if config[i] then 605 | config[i+10] = config[i] 606 | config[i].slot = i + 10 607 | config[i] = nil 608 | end 609 | end 610 | pdata.config_tmp.max_slot = pdata.config_tmp.max_slot + 10 611 | at_gui.update_sliders(pdata) 612 | at_gui.adjust_slots(pdata, start) 613 | at_gui.update_buttons(pdata) 614 | end 615 | end, 616 | 617 | move_button = function(args) 618 | local player_index = args.player_index 619 | local player = game.get_player(player_index) 620 | local pdata = global._pdata[player_index] 621 | local index = tonumber(args.parameter) 622 | if index then 623 | pdata.main_button_index = index 624 | at_gui.init_main_button(player, pdata, true) 625 | else 626 | player.print{"at-message.invalid-index"} 627 | end 628 | end, 629 | 630 | mess_up = function(player) 631 | player.gui.relative.clear() 632 | player.gui.screen.clear() 633 | end, 634 | } 635 | 636 | commands.add_command("at_import", {"at-commands.import"}, at_commands.import) 637 | commands.add_command("at_reset", {"at-commands.reset"}, at_commands.reset) 638 | commands.add_command("at_compress", {"at-commands.compress"}, at_commands.compress) 639 | commands.add_command("at_insert_row", {"at-commands.insert_row"}, at_commands.insert_row) 640 | -------------------------------------------------------------------------------- /data.lua: -------------------------------------------------------------------------------- 1 | local data_util = require("__flib__.data-util") 2 | require("prototypes.styles") 3 | 4 | local tint = {r=255, g=240, b=0} 5 | 6 | data:extend{ 7 | data_util.build_sprite("autotrash_selection", nil, data_util.planner_base_image, 64, 4, {tint=tint}) 8 | } 9 | 10 | data:extend { 11 | { 12 | type = "selection-tool", 13 | name = "autotrash-network-selection", 14 | icons = {{icon=data_util.planner_base_image, icon_size=64, icon_mipmaps=4, tint=tint}}, 15 | stack_size = 1, 16 | flags = {"hidden", "only-in-cursor", "not-stackable", "draw-logistic-overlay"}, 17 | draw_label_for_cursor_render = true, 18 | selection_color = { r = 0, g = 1, b = 0 }, 19 | alt_selection_color = { r = 0, g = 0, b = 1 }, 20 | selection_mode = {"blueprint"}, 21 | alt_selection_mode = {"blueprint"}, 22 | selection_cursor_box_type = "copy", 23 | alt_selection_cursor_box_type = "copy", 24 | entity_type_filters = {"roboport"}, 25 | alt_entity_type_filters = {"roboport"}, 26 | } 27 | } 28 | 29 | data:extend{ 30 | { 31 | type = "custom-input", 32 | name = "autotrash-toggle-gui", 33 | key_sequence = "CONTROL + L", 34 | order = "a" 35 | }, 36 | { 37 | type = "custom-input", 38 | name = "autotrash_trash_cursor", 39 | key_sequence = "SHIFT + T", 40 | order = "b" 41 | }, 42 | { 43 | type = "custom-input", 44 | name = "autotrash_pause", 45 | key_sequence = "SHIFT + P", 46 | order = "c" 47 | }, 48 | { 49 | type = "custom-input", 50 | name = "autotrash_pause_requests", 51 | key_sequence = "SHIFT + O", 52 | order = "d" 53 | }, 54 | { 55 | type = "custom-input", 56 | name = "autotrash-toggle-unrequested", 57 | key_sequence = "", 58 | order = "e" 59 | }, 60 | } 61 | 62 | data:extend{ 63 | { 64 | type = "shortcut", 65 | name = "autotrash-toggle-gui", 66 | action = "lua", 67 | icon = data_util.build_sprite(nil, nil, "__AutoTrash__/graphics/shortcut.png", 64), 68 | disabled_icon = data_util.build_sprite(nil, nil, "__AutoTrash__/graphics/shortcut-disabled.png", 64), 69 | small_icon = data_util.build_sprite(nil, nil, "__AutoTrash__/graphics/shortcut.png", 64), 70 | disabled_small_icon = data_util.build_sprite(nil, nil, "__AutoTrash__/graphics/shortcut-disabled.png", 64), 71 | toggleable = true, 72 | associated_control_input = "autotrash-toggle-gui" 73 | } 74 | } -------------------------------------------------------------------------------- /graphics/frame-action-icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Choumiko/AutoTrash/4c45ef066720670a4ac23f6d4dd0d7a58af3b27c/graphics/frame-action-icons.png -------------------------------------------------------------------------------- /graphics/gui2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Choumiko/AutoTrash/4c45ef066720670a4ac23f6d4dd0d7a58af3b27c/graphics/gui2.png -------------------------------------------------------------------------------- /graphics/rip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Choumiko/AutoTrash/4c45ef066720670a4ac23f6d4dd0d7a58af3b27c/graphics/rip.png -------------------------------------------------------------------------------- /graphics/shortcut-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Choumiko/AutoTrash/4c45ef066720670a4ac23f6d4dd0d7a58af3b27c/graphics/shortcut-disabled.png -------------------------------------------------------------------------------- /graphics/shortcut.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Choumiko/AutoTrash/4c45ef066720670a4ac23f6d4dd0d7a58af3b27c/graphics/shortcut.png -------------------------------------------------------------------------------- /info.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "AutoTrash", 3 | "version": "5.3.16", 4 | "factorio_version": "1.1", 5 | "title": "Auto Trash", 6 | "author": "Choumiko", 7 | "contact": "Discord: Choumiko#6179", 8 | "homepage": "https://github.com/Choumiko/AutoTrash", 9 | "description": "Allows saving of different logistic request setups\nEnable autotrash only in certain logistic networks\nLoad your spidertron with different setups with a single click\nLoad one or more preset after respawning\nTrash unrequested items", 10 | "dependencies": ["flib >= 0.6.0"], 11 | "package":{ 12 | "scripts": { 13 | }, 14 | "ignore":[ "*.zip", "*.xcf" ] 15 | } 16 | } -------------------------------------------------------------------------------- /locale/en/en.cfg: -------------------------------------------------------------------------------- 1 | [mod-name] 2 | AutoTrash=Auto Trash 3 | [mod-description] 4 | AutoTrash=Allows saving of different logistic request setups\nCan enable autotrash only in a certain logistics network\nKeeps your inventory clean of certain items 5 | [item-name] 6 | autotrash-network-selection=Network selection 7 | [at-gui] 8 | logistics-configuration=Logistics configuration 9 | presets=Presets 10 | trash=Trash 11 | request=Request 12 | pause-trash=Pause Autotrash 13 | pause-requests=Pause Requests 14 | status-display=Show status display 15 | trash-above-requested=Autotrash above requests 16 | trash-unrequested=Autotrash unrequested items 17 | trash-in-main-network=Autotrash only in main networks 18 | tooltip-pause-trash=(__CONTROL__autotrash_pause__) 19 | tooltip-pause-requests=(__CONTROL__autotrash_pause_requests__) 20 | tooltip-rip=Load preset when respawning 21 | tooltip-export=Export to a blueprint string.\nHold shift to create a blueprint 22 | tooltip-export-all=Export to a blueprint string.\nHold shift to create a blueprint book 23 | tooltip-import=Import from a blueprint string.\nClick with a blueprint to import directly 24 | tooltip-import-all=Import from a blueprint string.\nClick with a blueprint or blueprint book to import directly\n[color=red][font=default-bold]Caution: Will overwrite existing presets[/font][/color] 25 | tooltip-main-button=__CONTROL_STYLE_BEGIN__Ctrl + Right click __CONTROL_STYLE_END__ to toggle the status display.\nStatus display: __1__ 26 | tooltip-quick-actions=Quick actions apply to all items 27 | keep-open=Keep open 28 | quick-actions=Quick actions 29 | clear-requests=Clear requests 30 | clear-trash=Clear trash 31 | clear-both=Clear both 32 | trash-to-requests=Set trash to requests 33 | requests-to-trash=Set requests to trash 34 | import-requests=Load from personal logistics 35 | import-from-inventory=Load from inventory 36 | tooltip-add-network=Add current to main networks 37 | tooltip-remove-network=Remove current from main networks 38 | tooltip-edit-networks=Edit networks 39 | tooltip-selection-tool=Select roboports with the tool to add them to networks\nShift + select to remove them. 40 | tooltip-show-network=Show in map 41 | spider-save=Save the spidertrons logistic requests as a preset 42 | spider-trash-all=Trash unrequested items 43 | spider-trash-all-tt=Sets every unconfigured item to be trashed 44 | autotoggle_unrequested=Auto-off 45 | autotoggle_unrequested_tt=Turn off trashing of unrequested items once they are all removed 46 | 47 | [at-message] 48 | name-not-set=You must enter a name! 49 | name-in-use=This name is already used! 50 | not-in-network=No network found! 51 | no-network-set=No network set. 52 | network-unset=Autotrash network #__1__ has been unset. 53 | network-lost=AutoTrash lost track of network #__1__ 54 | import-error=Invalid AutoTrash configuration. Only blueprints/strings created with AutoTrash can be imported. 55 | no-character=Auto Trash: No character, closing gui. 56 | invalid-gui=Auto Trash: The GUI has become invalid. This likely means that another mod tampered with it. 57 | no-presets-to-export=No presets to export. 58 | preset-updated=Preset "__1__" updated. 59 | added-to-temporary-trash=Added __1__ to temporary trash. 60 | removed-from-temporary-requests=Removed __1__ from temporary requests. 61 | character-needed=Personal logistic requests are only possible when using a character. 62 | empty-cursor-needed=Click with an empty cursor. 63 | trash-paused=Autotrash paused 64 | trash-unpaused=Autotrash unpaused 65 | adjusted-trash-amount=Adjusted trash amount for __1__ to __2__ 66 | added-network=Added network #__1__ 67 | removed-network=Removed network #__1__ 68 | network-exists=Network #__1__ already added 69 | merged-networks=Merged network #__1__ and #__2__ 70 | empty-config=Autotrash configuration is empty. 71 | auto-import=Imported configuration from the character UI. 72 | invalid-index=Invalid index 73 | 74 | [at-commands] 75 | import=- Import logistic requests from the vanilla gui 76 | reset=- Reset the gui 77 | compress=- Remove empty rows in the logistics configuration 78 | insert_row= - Insert an empty row after row # 79 | 80 | [controls] 81 | autotrash_pause=Pause Autotrash 82 | autotrash_pause_requests=Pause Requests 83 | autotrash_trash_cursor=Trash item on cursor 84 | autotrash-toggle-gui=Open Auto Trash 85 | autotrash-toggle-unrequested=Toggle trashing unrequested items 86 | [shortcut-name] 87 | autotrash-toggle-gui=Open Auto Trash 88 | 89 | [mod-setting-name] 90 | autotrash_trash_equals_requests=Set trash to request amount 91 | autotrash_gui_displayed_columns=Columns to display 92 | autotrash_gui_rows_before_scroll=Rows before scrolling 93 | autotrash_display_messages=Display messages 94 | autotrash_reset_on_close=Reset on close 95 | autotrash_close_on_apply=Close on apply 96 | autotrash_overwrite=Overwrite presets 97 | autotrash_update_rate=Update rate (ticks) 98 | autotrash_status_count=Item status display 99 | autotrash_status_columns=Item status columns 100 | autotrash_open_library=Open the blueprint library after exporting 101 | autotrash_show_button=Show Auto Trash button 102 | 103 | [mod-setting-description] 104 | autotrash_trash_equals_requests=Sets the maximum amount of items to the default request amount 105 | autotrash_gui_displayed_columns=Number of columns in the configuration window 106 | autotrash_gui_rows_before_scroll=Number of rows before the gui adds a scrollbar 107 | autotrash_display_messages=Whether to show some informational messages 108 | autotrash_reset_on_close=Restore the last applied configuration when closing the gui with unsaved changes 109 | autotrash_close_on_apply=Close the gui after applying changes 110 | autotrash_overwrite=Allow overwriting existing presets 111 | autotrash_update_rate=Update the request status every X ticks 112 | autotrash_status_count=Number of items to display in the status window 113 | autotrash_status_columns=Number of columns in the status display 114 | autotrash_open_library=Open the blueprint library after exporting to a blueprint -------------------------------------------------------------------------------- /migrations/2020_12_15_AutoTrash_5.3.9.lua: -------------------------------------------------------------------------------- 1 | global.unlocked_by_force = global.unlocked_by_force or {} -------------------------------------------------------------------------------- /prototypes/styles.lua: -------------------------------------------------------------------------------- 1 | local data_util = require("__flib__.data-util") 2 | 3 | local frame_action_icons = "__AutoTrash__/graphics/frame-action-icons.png" 4 | 5 | data:extend{ 6 | -- frame action icons 7 | data_util.build_sprite("at_pin_black", {0, 64}, frame_action_icons, 32), 8 | data_util.build_sprite("at_pin_white", {32, 64}, frame_action_icons, 32), 9 | data_util.build_sprite("at_import_string", nil, "__base__/graphics/icons/shortcut-toolbar/mip/import-string-x24.png", 24, 2, {scale = 0.5}), 10 | data_util.build_sprite("autotrash_trash", {0, 0}, "__AutoTrash__/graphics/gui2.png", 128), 11 | data_util.build_sprite("autotrash_rip", nil, "__AutoTrash__/graphics/rip.png", 64), 12 | } 13 | 14 | local base_layer = { 15 | filename = "__AutoTrash__/graphics/gui2.png", 16 | size = 128, 17 | position = {0, 0} 18 | } 19 | 20 | data:extend{ 21 | { 22 | type = "sprite", 23 | name = "autotrash_trash_paused", 24 | flags = {"icon"}, 25 | layers = { 26 | base_layer, 27 | {filename = "__AutoTrash__/graphics/gui2.png", size = 128, position = {128, 0}} 28 | } 29 | }, 30 | { 31 | type = "sprite", 32 | name = "autotrash_requests_paused", 33 | flags = {"icon"}, 34 | layers = { 35 | base_layer, 36 | {filename = "__AutoTrash__/graphics/gui2.png", size = 128, position = {0, 128}} 37 | } 38 | }, 39 | { 40 | type = "sprite", 41 | name = "autotrash_both_paused", 42 | flags = {"icon"}, 43 | layers = { 44 | base_layer, 45 | {filename = "__AutoTrash__/graphics/gui2.png", size = 128, position = {128, 128}} 46 | } 47 | }, 48 | } 49 | 50 | local styles = data.raw["gui-style"].default 51 | 52 | styles["at_request_label_bottom"] = { 53 | type = "label_style", 54 | parent = "count_label", 55 | height = 36, 56 | width = 36, 57 | vertical_align = "bottom", 58 | horizontal_align = "right", 59 | right_padding = 2 60 | } 61 | styles["at_request_label_top"] = { 62 | type = "label_style", 63 | parent = "at_request_label_bottom", 64 | vertical_align = "top", 65 | } 66 | 67 | styles["at_save_button"] = { 68 | type = "button_style", 69 | parent = "button", 70 | width = 60 71 | } 72 | 73 | styles["at_preset_button"] = { 74 | type = "button_style", 75 | parent = "button", 76 | width = 150, 77 | } 78 | 79 | local button = styles.button 80 | 81 | styles["at_selected_tool_button"] = { 82 | type = "button_style", 83 | parent = "tool_button", 84 | default_font_color = button.selected_font_color, 85 | default_graphical_set = button.selected_graphical_set, 86 | 87 | hovered_font_color = button.selected_hovered_font_color, 88 | hovered_graphical_set = button.selected_hovered_graphical_set, 89 | 90 | clicked_font_color = button.selected_clicked_font_color, 91 | clicked_vertical_offset = 1, -- text/icon goes down on click 92 | clicked_graphical_set = button.selected_clicked_graphical_set, 93 | } 94 | 95 | styles["at_preset_button_selected"] = { 96 | type = "button_style", 97 | parent = "at_preset_button", 98 | default_font_color = button.selected_font_color, 99 | default_graphical_set = button.selected_graphical_set, 100 | 101 | hovered_font_color = button.selected_hovered_font_color, 102 | hovered_graphical_set = button.selected_hovered_graphical_set, 103 | 104 | clicked_font_color = button.selected_clicked_font_color, 105 | clicked_vertical_offset = 1, -- text/icon goes down on click 106 | clicked_graphical_set = button.selected_clicked_graphical_set, 107 | } 108 | 109 | styles["at_delete_preset"] = { 110 | type = "button_style", 111 | parent = "tool_button_red", 112 | padding = 0 113 | } 114 | 115 | styles["at_preset_button_small"] = { 116 | type = "button_style", 117 | parent = "tool_button", 118 | padding = 0 119 | } 120 | 121 | styles["at_preset_button_small_selected"] = { 122 | type = "button_style", 123 | parent = "at_preset_button_small", 124 | default_font_color = button.selected_font_color, 125 | default_graphical_set = button.selected_graphical_set, 126 | 127 | hovered_font_color = button.selected_hovered_font_color, 128 | hovered_graphical_set = button.selected_hovered_graphical_set, 129 | 130 | clicked_font_color = button.selected_clicked_font_color, 131 | clicked_vertical_offset = 1, -- text/icon goes down on click 132 | clicked_graphical_set = button.selected_clicked_graphical_set, 133 | } 134 | 135 | styles["at_request_status_table"] = { 136 | type = "table_style", 137 | horizontal_spacing = 1, 138 | vertical_spacing = 1 139 | } 140 | 141 | styles["at_quick_actions"] = { 142 | type = "dropdown_style", 143 | minimal_width = 216 144 | } 145 | 146 | styles["at_slider_flow"] = { 147 | type = "horizontal_flow_style", 148 | vertical_align = "center" 149 | } 150 | 151 | styles["at_slot_table_scroll_pane"] = { 152 | type = "scroll_pane_style", 153 | extra_padding_when_activated = 0, 154 | background_graphical_set = 155 | { 156 | position = {282, 17}, 157 | corner_size = 8, 158 | overall_tiling_horizontal_padding = 4, 159 | overall_tiling_horizontal_size = 32, 160 | overall_tiling_horizontal_spacing = 8, 161 | overall_tiling_vertical_padding = 4, 162 | overall_tiling_vertical_size = 32, 163 | overall_tiling_vertical_spacing = 8 164 | }, 165 | } 166 | 167 | styles["at_filter_group_table"] = { 168 | type = "table_style", 169 | horizontal_spacing = 0, 170 | vertical_spacing = 0, 171 | padding = 0, 172 | } 173 | 174 | styles["at_right_container_flow"] = { 175 | type = "vertical_flow_style", 176 | padding = 12, 177 | top_padding = 8, 178 | vertical_spacing = 10, 179 | width = 246 180 | } 181 | 182 | styles["at_right_scroll_pane"] = { 183 | type = "scroll_pane_style", 184 | extra_padding_when_activated = 0, 185 | vertical_flow_style = { 186 | type = "vertical_flow_style", 187 | horizontally_stretchable = "on", 188 | vertically_stretchable = "on", 189 | padding = 4 190 | } 191 | } 192 | 193 | styles["at_right_flow_in_scroll_pane"] = { 194 | type = "vertical_flow_style", 195 | horizontally_stretchable = "on", 196 | left_padding = 4, 197 | right_padding = 4, 198 | top_padding = 4, 199 | } 200 | 201 | styles["at_bordered_frame"] = { 202 | type = "frame_style", 203 | parent = "bordered_frame", 204 | right_padding = 8, 205 | horizontally_stretchable = "on", 206 | } 207 | 208 | styles["at_bordered_frame2"] = { 209 | type = "frame_style", 210 | parent = "at_bordered_frame", 211 | height = 112, 212 | vertical_flow_style = { 213 | type = "vertical_flow_style", 214 | vertical_align = "bottom" 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /scripts/global-data.lua: -------------------------------------------------------------------------------- 1 | local constants = require("constants") 2 | local global_data = {} 3 | 4 | function global_data.init() 5 | global._pdata = {} 6 | global.unlocked_by_force = {} 7 | global.trash_all_items = {} 8 | end 9 | 10 | function global_data.refresh() 11 | local all_trash = {config = {}, by_name = {}, max_slot = 0, c_requests = 0} 12 | local all = {config = {}, by_name = {}, max_slot = 0, c_requests = 0} 13 | local filters = { 14 | {filter = "selection-tool", invert = true, mode = "and"}, 15 | {filter = "type", type = "blueprint", invert = true, mode = "and"}, 16 | {filter = "type", type = "blueprint-book", invert = true, mode = "and"}, 17 | {filter = "type", type = "deconstruction-item", invert = true, mode = "and"}, 18 | {filter = "type", type = "upgrade-item", invert = true, mode = "and"}, 19 | {filter = "type", type = "copy-paste-tool", invert = true, mode = "and"}, 20 | {filter = "type", type = "selection-tool", invert = true, mode = "and"}, 21 | {filter = "flag", flag = "hidden", invert = true, mode = "and"}, 22 | --{filter = "place-result", mode = "and"} 23 | } 24 | local i = 0 25 | local group, subgroup 26 | local max_request = constants.max_request 27 | for name, proto in pairs(game.get_filtered_item_prototypes(filters)) do 28 | if group and group ~= proto.group.name and i % 10 > 0 then 29 | i = i + 1 30 | i = math.ceil(i/10) * 10 + 11 31 | elseif subgroup and subgroup ~= proto.subgroup.name then 32 | i = i + 1 33 | i = math.ceil(i/10) * 10 + 1 34 | else 35 | i = i + 1 36 | end 37 | 38 | subgroup = proto.subgroup.name 39 | group = proto.group.name 40 | all.config[i] = {name = name, min = 0, max = max_request, slot = i} 41 | all.by_name[name] = all.config[i] 42 | all_trash.config[i] = {name = name, min = 0, max = 0, slot = i} 43 | all_trash.by_name[name] = all_trash.config[i] 44 | end 45 | all_trash.max_slot = i - 1 46 | global.trash_all_items = all_trash 47 | global.all_items = all 48 | end 49 | 50 | return global_data -------------------------------------------------------------------------------- /scripts/gui-util.lua: -------------------------------------------------------------------------------- 1 | local gui_util = {} 2 | 3 | gui_util.frame_action_button = function(sprite, hovered_sprite, action, ref, params) 4 | local ret = {type = "sprite-button", style = "frame_action_button", mouse_button_filter={"left"}, 5 | sprite = sprite, 6 | clicked_sprite = hovered_sprite, 7 | hovered_sprite = hovered_sprite, 8 | actions = {on_click = action}, 9 | ref = ref 10 | } 11 | if params then 12 | for k, v in pairs(params) do 13 | ret[k] = v 14 | end 15 | end 16 | return ret 17 | end 18 | 19 | gui_util.pushers = { 20 | horizontal = {type = "empty-widget", style_mods = {horizontally_stretchable = true}}, 21 | vertical = {type = "empty-widget", style_mods = {vertically_stretchable = true}} 22 | } 23 | 24 | gui_util.preset = function(preset_name, pdata) 25 | local style = pdata.selected_presets[preset_name] and "at_preset_button_selected" or "at_preset_button" 26 | local rip_style = pdata.death_presets[preset_name] and "at_preset_button_small_selected" or "at_preset_button_small" 27 | return {type = "flow", direction = "horizontal", name = preset_name, children = { 28 | {type = "button", style = style, caption = preset_name, name = preset_name, 29 | actions = {on_click = {gui = "presets", action = "load"}}, 30 | }, 31 | {type = "sprite-button", style = rip_style, sprite = "autotrash_rip", 32 | tooltip = {"at-gui.tooltip-rip"}, 33 | actions = {on_click = {gui = "presets", action = "change_death_preset"}}, 34 | }, 35 | {type = "sprite-button", style = "at_delete_preset", sprite = "utility/trash", 36 | actions = {on_click = {gui = "presets", action = "delete"}}, 37 | } 38 | }} 39 | end 40 | 41 | gui_util.presets = function(pdata) 42 | local ret = {} 43 | local i = 1 44 | for name in pairs(pdata.presets) do 45 | ret[i] = gui_util.preset(name, pdata) 46 | i = i + 1 47 | end 48 | return ret 49 | end 50 | 51 | return gui_util -------------------------------------------------------------------------------- /scripts/gui.lua: -------------------------------------------------------------------------------- 1 | local gui = require("__flib__.gui-beta") 2 | local mod_gui = require ("__core__.lualib.mod-gui") 3 | 4 | local constants = require("constants") 5 | local presets = require("scripts.presets") 6 | 7 | local spider_gui = require("scripts.spidertron") 8 | local at_util = require("scripts.util") 9 | local gui_util = require("scripts.gui-util") 10 | local player_data = require("scripts.player-data") 11 | local format_number = at_util.format_number 12 | local item_prototype = at_util.item_prototype 13 | local in_network = at_util.in_network 14 | local get_non_equipment_network = at_util.get_non_equipment_network 15 | 16 | local function clamp(v, min, max) 17 | return (v < min) and min or (v > max and max or v) 18 | end 19 | 20 | local function format_request(item_config) 21 | return (item_config.min and item_config.min >= 0) and item_config.min or (item_config.max and 0) 22 | end 23 | 24 | local function format_trash(item_config) 25 | return (item_config.max < constants.max_request) and format_number(item_config.max, true) or "∞" 26 | end 27 | 28 | local function tonumber_max(n) 29 | n = tonumber(n) or 0 30 | return (n > constants.max_request) and constants.max_request or n 31 | end 32 | 33 | local function get_network_data(player) 34 | local character = player.character 35 | if not character then return end 36 | local network = get_non_equipment_network(character) 37 | local requester = character.get_logistic_point(defines.logistic_member_index.character_requester) 38 | if not (network and requester and network.valid and requester.valid) then 39 | return false 40 | end 41 | local on_the_way = requester.targeted_items_deliver 42 | local item_count = player.get_main_inventory().get_contents() 43 | local cursor_stack = player.cursor_stack 44 | cursor_stack = (cursor_stack and cursor_stack.valid_for_read) and {[cursor_stack.name] = cursor_stack.count} or {} 45 | local get_inventory, inventory = player.get_inventory, defines.inventory 46 | local armor = get_inventory(inventory.character_armor).get_contents() 47 | local gun = get_inventory(inventory.character_guns).get_contents() 48 | local ammo = get_inventory(inventory.character_ammo).get_contents() 49 | 50 | return true, on_the_way, item_count, cursor_stack, armor, gun, ammo 51 | end 52 | 53 | local at_gui = { 54 | defines = { 55 | trash_above_requested = "trash_above_requested", 56 | trash_unrequested = "trash_unrequested", 57 | trash_network = "trash_network", 58 | pause_trash = "pause_trash", 59 | pause_requests = "pause_requests", 60 | network_button = "network_button", 61 | status_display = "status_display", 62 | autotoggle_unrequested = "autotoggle_unrequested", 63 | }, 64 | } 65 | 66 | local function import_presets(player, pdata, add_presets, stack) 67 | if stack and stack.valid_for_read then 68 | if stack.is_blueprint and stack.is_blueprint_setup() then 69 | local preset, cc_found, missing_items = presets.import(stack.get_blueprint_entities(), stack.blueprint_icons) 70 | if cc_found then 71 | pdata.config_tmp = preset 72 | player.print({"string-import-successful", "AutoTrash configuration"}) 73 | pdata.selected = false 74 | at_gui.adjust_slots(pdata) 75 | at_gui.update_buttons(pdata) 76 | at_gui.update_sliders(pdata) 77 | at_gui.mark_dirty(player, pdata) 78 | --named preset 79 | if stack.label and stack.label ~= "AutoTrash_configuration" then 80 | local textfield = pdata.gui.presets.textfield 81 | local preset_name = string.sub(stack.label, 11) 82 | if add_presets and player_data.add_preset(player, pdata, preset_name, preset) then 83 | pdata.selected_presets = {[preset_name] = true} 84 | at_gui.update_presets(player, pdata) 85 | end 86 | textfield.text = preset_name 87 | player.clear_cursor() 88 | textfield.focus() 89 | end 90 | else 91 | player.print({"", {"error-while-importing-string"}, " ", {"at-message.import-error"}}) 92 | if #missing_items > 0 then 93 | player.print("Removed missing items from import: ", table.concat(missing_items, ", ")) 94 | end 95 | end 96 | return true 97 | elseif add_presets and stack.is_blueprint_book then 98 | local book_inventory = stack.get_inventory(defines.inventory.item_main) 99 | local any_cc = false 100 | for i = 1, #book_inventory do 101 | local bp = book_inventory[i] 102 | if bp.valid_for_read and bp.is_blueprint_setup() then 103 | local config, cc, missing_items = presets.import(bp.get_blueprint_entities(), bp.blueprint_icons) 104 | if cc then 105 | any_cc = true 106 | player_data.add_preset(player, pdata, bp.label, config) 107 | end 108 | if #missing_items > 0 then 109 | player.print("Removed missing items from import: ", table.concat(missing_items, ", ")) 110 | end 111 | end 112 | end 113 | if any_cc then 114 | player.print({"string-import-successful", {"at-gui.presets"}}) 115 | at_gui.update_presets(player, pdata) 116 | else 117 | player.print({"", {"error-while-importing-string"}, " ", {"at-message.import-error"}}) 118 | end 119 | return true 120 | end 121 | end 122 | return false 123 | end 124 | 125 | at_gui.toggle_setting = { 126 | trash_above_requested = function(player, pdata) 127 | at_util.set_requests(player, pdata) 128 | return pdata.flags.trash_above_requested 129 | end, 130 | trash_unrequested = function(player, pdata) 131 | at_util.set_requests(player, pdata) 132 | return pdata.flags.trash_unrequested 133 | end, 134 | autotoggle_unrequested = function(player, pdata) 135 | at_util.set_requests(player, pdata) 136 | at_gui.update_options(pdata) 137 | return pdata.flags.autotoggle_unrequested 138 | end, 139 | trash_network = function(player, pdata) 140 | if pdata.flags.trash_network and not next(pdata.networks) then 141 | player.print{"at-message.no-network-set"} 142 | pdata.flags.trash_network = false 143 | return false 144 | end 145 | if pdata.flags.trash_network and in_network(player, pdata) then 146 | at_util.unpause_trash(player, pdata) 147 | at_gui.update_main_button(player, pdata) 148 | end 149 | return pdata.flags.trash_network 150 | end, 151 | pause_trash = function(player, pdata) 152 | if pdata.flags.pause_trash then 153 | at_util.pause_trash(player, pdata) 154 | else 155 | at_util.unpause_trash(player, pdata) 156 | end 157 | at_gui.update_main_button(player, pdata) 158 | return pdata.flags.pause_trash 159 | end, 160 | pause_requests = function(player, pdata) 161 | if pdata.flags.pause_requests then 162 | at_util.pause_requests(player, pdata) 163 | else 164 | at_util.unpause_requests(player, pdata) 165 | end 166 | at_gui.update_main_button(player, pdata) 167 | at_gui.update_status_display(player, pdata) 168 | return pdata.flags.pause_requests 169 | end, 170 | } 171 | 172 | at_gui.templates = { 173 | slot_table = { 174 | main = function(btns, pdata) 175 | local ret = {type = "table", column_count = pdata.settings.columns, 176 | style = "at_filter_group_table", 177 | style_mods = { 178 | minimal_height = pdata.settings.rows * 40 179 | }, 180 | ref = {"main", "slot_table"}, 181 | children = {} 182 | } 183 | for i=1, btns do 184 | ret.children[i] = at_gui.templates.slot_table.button(i, pdata) 185 | end 186 | return ret 187 | end, 188 | button = function(i, pdata) 189 | local style = (i == pdata.selected) and "yellow_slot_button" or "slot_button" 190 | local config = pdata.config_tmp.config[i] 191 | local req = "" 192 | local trash = "" 193 | if config then 194 | if config.min == config.max then 195 | req = "" 196 | trash = format_number(format_request(config), true) or "" 197 | else 198 | req = format_number(format_request(config), true) or "" 199 | trash = format_trash(config) 200 | end 201 | end 202 | return {type = "choose-elem-button", name = i, elem_mods = {elem_value = config and config.name, locked = config and i ~= pdata.selected}, 203 | elem_type = "item", style = style, 204 | actions = { 205 | on_click = {gui = "slots", action = "item_button_click"}, 206 | on_elem_changed = {gui = "slots", action = "item_button"} 207 | }, 208 | children = { 209 | {type = "label", style = "at_request_label_top", ignored_by_interaction = true, caption = req}, 210 | {type = "label", style = "at_request_label_bottom", ignored_by_interaction = true, caption = trash} 211 | } 212 | } 213 | end, 214 | }, 215 | 216 | options = function(flags) 217 | local toggle_action = {on_checked_state_changed = {gui = "settings", action = "toggle"}} 218 | return { 219 | { 220 | type = "checkbox", 221 | name = at_gui.defines.trash_above_requested, 222 | caption = {"at-gui.trash-above-requested"}, 223 | state = flags.trash_above_requested, 224 | actions = toggle_action, 225 | 226 | }, 227 | {type = "flow", direction = "horizontal", style_mods = {horizontal_spacing = 20}, children = { 228 | { 229 | type = "checkbox", 230 | ref = {"options", "trash_unrequested"}, 231 | name = at_gui.defines.trash_unrequested, 232 | caption = {"at-gui.trash-unrequested"}, 233 | state = flags.trash_unrequested, 234 | actions = toggle_action, 235 | }, 236 | { 237 | type = "checkbox", 238 | ref = {"options", "autotoggle_unrequested"}, 239 | name = at_gui.defines.autotoggle_unrequested, 240 | caption = {"at-gui.autotoggle_unrequested"}, 241 | state = flags.autotoggle_unrequested, 242 | tooltip = {"at-gui.autotoggle_unrequested_tt"}, 243 | actions = toggle_action, 244 | }, 245 | }}, 246 | { 247 | type = "checkbox", 248 | name = at_gui.defines.trash_network, 249 | caption = {"at-gui.trash-in-main-network"}, 250 | state = flags.trash_network, 251 | actions = toggle_action, 252 | }, 253 | { 254 | type = "checkbox", 255 | name = at_gui.defines.pause_trash, 256 | caption = {"at-gui.pause-trash"}, 257 | tooltip = {"at-gui.tooltip-pause-trash"}, 258 | state = flags.pause_trash, 259 | actions = toggle_action, 260 | }, 261 | { 262 | type = "checkbox", 263 | name = at_gui.defines.pause_requests, 264 | caption = {"at-gui.pause-requests"}, 265 | tooltip = {"at-gui.tooltip-pause-requests"}, 266 | state = flags.pause_requests, 267 | actions = toggle_action, 268 | }, 269 | { 270 | type = "checkbox", 271 | name = at_gui.defines.status_display, 272 | caption = {"at-gui.status-display"}, 273 | state = flags.status_display_open, 274 | actions = {on_checked_state_changed = {gui = "settings", action = "toggle_status_display"}}, 275 | }, 276 | { 277 | type = "flow", style_mods = {vertical_align = "center"}, children = { 278 | {type = "label", caption = "Main networks: "}, 279 | {type = "button", caption = "+", style = "tool_button", 280 | tooltip = {"at-gui.tooltip-add-network"}, 281 | actions = {on_click = {gui = "settings", action = "add_network"}}, 282 | }, 283 | {type = "button", caption = "-", style = "tool_button", 284 | tooltip = {"at-gui.tooltip-remove-network"}, 285 | actions = {on_click = {gui = "settings", action = "remove_network"}}, 286 | }, 287 | {type = "sprite-button", sprite = "utility/rename_icon_normal", style = "tool_button", 288 | ref = {"main", "network_edit_button"}, 289 | actions = {on_click = {gui = "settings", action = "edit_networks"}}, 290 | tooltip = {"at-gui.tooltip-edit-networks"} 291 | }, 292 | } 293 | }, 294 | } 295 | end, 296 | 297 | networks = function(pdata) 298 | local ret = {} 299 | local i = 1 300 | for id, network in pairs(pdata.networks) do 301 | if network and network.valid then 302 | ret[i] = {type = "flow", name = id, direction = "horizontal", style_mods = {width = constants.gui_dimensions.network_flow}, 303 | children = { 304 | {type = "label", caption = {"", {"gui-logistic.network"}, " #" .. id}}, 305 | gui_util.pushers.horizontal, 306 | {type = "sprite-button", style = "tool_button", sprite = "utility/map", 307 | actions = {on_click = {gui = "networks", action = "view"}}, 308 | tooltip = {"at-gui.tooltip-show-network"} 309 | }, 310 | {type = "sprite-button", style = "tool_button", sprite = "utility/trash", 311 | actions = {on_click = {gui = "networks", action = "remove"}}, 312 | } 313 | } 314 | } 315 | i = i + 1 316 | end 317 | end 318 | return ret 319 | end, 320 | } 321 | 322 | at_gui.handlers = {} 323 | 324 | at_gui.handlers.main = { 325 | mod_gui_button = function(e) 326 | if e.button == defines.mouse_button_type.right then 327 | if e.control then 328 | at_gui.toggle_status_display(e.player, e.pdata) 329 | end 330 | elseif e.button == defines.mouse_button_type.left then 331 | at_gui.toggle(e.player, e.pdata) 332 | end 333 | end, 334 | pin_button = function(e) 335 | local pdata = e.pdata 336 | if pdata.flags.pinned then 337 | pdata.gui.main.pin_button.style = "frame_action_button" 338 | pdata.gui.main.pin_button.sprite = "at_pin_white" 339 | pdata.flags.pinned = false 340 | pdata.gui.main.window.force_auto_center() 341 | e.player.opened = pdata.gui.main.window 342 | else 343 | pdata.gui.main.pin_button.style = "flib_selected_frame_action_button" 344 | pdata.gui.main.pin_button.sprite = "at_pin_black" 345 | pdata.flags.pinned = true 346 | pdata.gui.main.window.auto_center = false 347 | e.player.opened = nil 348 | end 349 | end, 350 | close_button = function(e) 351 | at_gui.close(e.player, e.pdata) 352 | end, 353 | window = function(e) 354 | if not e.pdata.flags.pinned then 355 | at_gui.close(e.player, e.pdata) 356 | end 357 | end, 358 | apply_changes = function(e) 359 | local player = e.player 360 | local pdata = e.pdata 361 | 362 | local adjusted = player_data.check_config(player, pdata) 363 | pdata.config_new = at_util.copy_preset(pdata.config_tmp) 364 | pdata.dirty = false 365 | pdata.gui.main.reset_button.enabled = false 366 | at_util.set_requests(player, pdata) 367 | if pdata.settings.close_on_apply then 368 | at_gui.close(player, pdata) 369 | end 370 | if adjusted then 371 | at_gui.update_buttons(pdata) 372 | end 373 | at_gui.update_status_display(player, pdata) 374 | end, 375 | reset = function(e) 376 | local pdata = e.pdata 377 | if pdata.flags.dirty then 378 | pdata.config_tmp = at_util.copy_preset(pdata.config_new) 379 | e.element.enabled = false 380 | pdata.selected_presets = {} 381 | pdata.selected = false 382 | pdata.flags.dirty = false 383 | at_gui.adjust_slots(pdata) 384 | at_gui.update_buttons(pdata) 385 | at_gui.update_sliders(pdata) 386 | at_gui.update_presets(e.player, pdata) 387 | end 388 | end, 389 | export = function(e) 390 | local player = e.player 391 | local pdata = e.pdata 392 | local name 393 | if table_size(pdata.selected_presets) == 1 then 394 | name = "AutoTrash_" .. next(pdata.selected_presets) 395 | else 396 | name = "AutoTrash_configuration" 397 | end 398 | local text = presets.export(pdata.config_tmp, name) 399 | if e.shift then 400 | local stack = player.cursor_stack 401 | if stack.valid_for_read then 402 | player.print({"at-message.empty-cursor-needed"}) 403 | return 404 | else 405 | if stack.import_stack(text) ~= 0 then 406 | player.print({"failed-to-import-string", name}) 407 | return 408 | end 409 | end 410 | else 411 | at_gui.create_import_window(player, pdata, text) 412 | end 413 | end, 414 | export_all = function(e) 415 | local player = e.player 416 | local pdata = e.pdata 417 | if not next(pdata.presets) then 418 | player.print{"at-message.no-presets-to-export"} 419 | return 420 | end 421 | local text = presets.export_all(pdata) 422 | if e.shift then 423 | local stack = player.cursor_stack 424 | if stack.valid_for_read then 425 | player.print{"at-message.empty-cursor-needed"} 426 | return 427 | else 428 | if stack.import_stack(text) ~= 0 then 429 | player.print{"failed-to-import-string"} 430 | return 431 | end 432 | end 433 | else 434 | at_gui.create_import_window(player, pdata, text, true) 435 | end 436 | end, 437 | import = function(e) 438 | local player = e.player 439 | local pdata = e.pdata 440 | if not import_presets(player, pdata, false, player.cursor_stack) then 441 | at_gui.create_import_window(player, pdata) 442 | end 443 | end, 444 | import_all = function(e) 445 | local player = e.player 446 | local pdata = e.pdata 447 | if not import_presets(player, pdata, true, player.cursor_stack) then 448 | at_gui.create_import_window(player, pdata, nil, true) 449 | end 450 | end, 451 | quick_actions = function(e) 452 | local pdata = e.pdata 453 | local element = e.element 454 | local index = element.selected_index 455 | if index == 1 then return end 456 | 457 | local config_tmp = pdata.config_tmp 458 | if index == 2 then 459 | for _, config in pairs(config_tmp.config) do 460 | config.min = 0 461 | end 462 | config_tmp.c_requests = 0 463 | elseif index == 3 then 464 | for _, config in pairs(config_tmp.config) do 465 | config.max = constants.max_request 466 | end 467 | elseif index == 4 then 468 | config_tmp.config = {} 469 | config_tmp.by_name = {} 470 | config_tmp.max_slot = 0 471 | config_tmp.c_requests = 0 472 | pdata.selected = false 473 | at_gui.adjust_slots(pdata) 474 | elseif index == 5 then 475 | for _, config in pairs(config_tmp.config) do 476 | if config.min >= 0 then 477 | config.max = config.min 478 | end 479 | end 480 | elseif index == 6 then 481 | local c = 0 482 | for _, config in pairs(config_tmp.config) do 483 | if config.max < constants.max_request then 484 | config.min = config.max 485 | c = config.min > 0 and c + 1 or c 486 | end 487 | end 488 | config_tmp.c_requests = c 489 | elseif index == 7 then 490 | pdata.config_tmp = at_util.get_requests(e.player.get_personal_logistic_slot, e.player.character.request_slot_count) 491 | pdata.selected = false 492 | elseif index == 8 then 493 | local contents = e.player.get_main_inventory().get_contents() 494 | config_tmp.config = {} 495 | config_tmp.by_name = {} 496 | config_tmp.max_slot = 0 497 | config_tmp.c_requests = 0 498 | pdata.selected = false 499 | local i = 1 500 | for name, count in pairs(contents) do 501 | if not constants.trash_blacklist[name] then 502 | player_data.add_config(pdata, name, count, count, i) 503 | i = i + 1 504 | end 505 | end 506 | end 507 | element.selected_index = 1 508 | at_gui.mark_dirty(e.player, pdata) 509 | at_gui.update_sliders(pdata) 510 | at_gui.update_buttons(pdata) 511 | end 512 | } 513 | at_gui.handlers.slots = { 514 | item_button_click = function(e) 515 | local player = e.player 516 | local pdata = e.pdata 517 | local elem_value = e.element.elem_value 518 | local old_selected = pdata.selected 519 | local index = tonumber(e.element.name) 520 | if e.button == defines.mouse_button_type.right then 521 | if not elem_value then 522 | pdata.selected = false 523 | at_gui.update_button(pdata, old_selected, pdata.gui.main.slot_table.children[old_selected]) 524 | at_gui.update_sliders(pdata) 525 | return 526 | end 527 | at_gui.clear_button(player, pdata, index, e.element) 528 | at_gui.adjust_slots(pdata) 529 | elseif e.button == defines.mouse_button_type.left then 530 | if e.shift then 531 | local config_tmp = pdata.config_tmp 532 | local cursor_ghost = player.cursor_ghost 533 | --pickup ghost 534 | if elem_value and not cursor_ghost and not player.cursor_stack.valid_for_read then 535 | pdata.selected = index 536 | player.cursor_ghost = elem_value 537 | --drop ghost 538 | elseif cursor_ghost and old_selected then 539 | local old_config = config_tmp.config[old_selected] 540 | if elem_value and old_config and cursor_ghost.name == old_config.name then 541 | player_data.swap_configs(pdata, old_selected, index) 542 | player.cursor_ghost = nil 543 | pdata.selected = index 544 | at_gui.mark_dirty(player, pdata) 545 | end 546 | if not old_config then 547 | pdata.selected = false 548 | old_selected = false 549 | player.cursor_ghost = nil 550 | end 551 | end 552 | at_gui.update_button(pdata, index, e.element) 553 | at_gui.update_button(pdata, old_selected) 554 | at_gui.adjust_slots(pdata) 555 | at_gui.update_button_styles(player, pdata)--TODO: only update changed buttons 556 | at_gui.update_sliders(pdata) 557 | else 558 | if not elem_value or old_selected == index then return end 559 | if player.cursor_ghost then 560 | local old_config = old_selected and pdata.config_tmp.config[old_selected] 561 | -- "interrupted" click-drag, reset value 562 | if elem_value and old_config and old_config.name == elem_value then 563 | e.element.elem_value = nil 564 | return 565 | end 566 | end 567 | pdata.selected = index 568 | if old_selected then 569 | local old = pdata.gui.main.slot_table.children[old_selected] 570 | at_gui.update_button(pdata, old_selected, old) 571 | end 572 | at_gui.update_button(pdata, index, e.element) 573 | at_gui.update_button_styles(player, pdata)--TODO: only update changed buttons 574 | at_gui.update_sliders(pdata) 575 | end 576 | end 577 | end, 578 | item_button = function(e) 579 | local player = e.player 580 | local pdata = e.pdata 581 | local old_selected = pdata.selected 582 | --dragging to an empty slot, on_gui_click raised later 583 | if player.cursor_ghost and old_selected then return end 584 | 585 | local elem_value = e.element.elem_value 586 | local index = tonumber(e.element.name) 587 | if elem_value then 588 | local config_tmp = pdata.config_tmp 589 | local item_config = config_tmp.config[index] 590 | if item_config and elem_value == item_config.name then return end 591 | local existing_config = config_tmp.by_name[elem_value] 592 | if existing_config and existing_config.slot ~= index then 593 | local i = existing_config.slot 594 | player.print({"cant-set-duplicate-request", item_prototype(elem_value).localised_name}) 595 | pdata.selected = i 596 | at_gui.update_button(pdata, i, pdata.gui.main.slot_table.children[i]) 597 | pdata.gui.main.config_rows.scroll_to_element(pdata.gui.main.slot_table.children[i], "top-third") 598 | if item_config then 599 | e.element.elem_value = item_config.name 600 | else 601 | e.element.elem_value = nil 602 | end 603 | at_gui.update_button_styles(player, pdata)--TODO: only update changed buttons 604 | at_gui.update_sliders(pdata) 605 | return 606 | end 607 | pdata.selected = index 608 | local request_amount = item_prototype(elem_value).default_request_amount 609 | local trash_amount = pdata.settings.trash_equals_requests and request_amount or constants.max_request 610 | player_data.add_config(pdata, elem_value, request_amount, trash_amount, index) 611 | 612 | at_gui.mark_dirty(player, pdata) 613 | at_gui.update_button(pdata, index, e.element) 614 | if old_selected then 615 | at_gui.update_button(pdata, old_selected, pdata.gui.main.slot_table.children[old_selected]) 616 | end 617 | at_gui.adjust_slots(pdata) 618 | at_gui.update_button_styles(player, pdata)--TODO: only update changed buttons 619 | at_gui.update_sliders(pdata) 620 | else 621 | at_gui.clear_button(player, pdata, index, e.element) 622 | at_gui.adjust_slots(pdata) 623 | end 624 | end 625 | } 626 | at_gui.handlers.presets = { 627 | save = function(e) 628 | local player = e.player 629 | local pdata = e.pdata 630 | local textfield = pdata.gui.presets.textfield 631 | local name = textfield.text 632 | if player_data.add_preset(player, pdata, name) then 633 | pdata.selected_presets = {[name] = true} 634 | at_gui.update_presets(player, pdata) 635 | else 636 | textfield.focus() 637 | end 638 | end, 639 | load = function(e) 640 | local player = e.player 641 | local pdata = e.pdata 642 | local name = e.element.caption 643 | if not e.shift then 644 | pdata.selected_presets = {[name] = true} 645 | pdata.config_tmp = at_util.copy_preset(pdata.presets[name]) 646 | pdata.selected = false 647 | pdata.gui.presets.textfield.text = name 648 | else 649 | local selected_presets = pdata.selected_presets 650 | if not selected_presets[name] then 651 | selected_presets[name] = true 652 | else 653 | selected_presets[name] = nil 654 | end 655 | local tmp = {config = {}, by_name = {}, max_slot = 0, c_requests = 0} 656 | for key, _ in pairs(selected_presets) do 657 | presets.merge(tmp, pdata.presets[key], e.control) 658 | end 659 | pdata.config_tmp = tmp 660 | pdata.selected = false 661 | end 662 | at_gui.adjust_slots(pdata) 663 | at_gui.update_buttons(pdata) 664 | at_gui.mark_dirty(player, pdata, true) 665 | at_gui.update_presets(player, pdata) 666 | at_gui.update_sliders(pdata) 667 | end, 668 | delete = function(e, msg) 669 | local pdata = e.pdata 670 | local parent = e.element.parent 671 | local name = parent.name 672 | parent.destroy() 673 | pdata.selected_presets[name] = nil 674 | pdata.death_presets[name] = nil 675 | pdata.presets[name] = nil 676 | if msg.spider then 677 | pdata.gui.presets.scroll[name].destroy() 678 | end 679 | at_gui.update_presets(e.player, pdata) 680 | end, 681 | change_death_preset = function(e) 682 | local pdata = e.pdata 683 | local name = e.element.parent.name 684 | if not (e.shift or e.control) then 685 | pdata.death_presets = {[name] = true} 686 | else 687 | local selected_presets = pdata.death_presets 688 | if not selected_presets[name] then 689 | selected_presets[name] = true 690 | else 691 | selected_presets[name] = nil 692 | end 693 | end 694 | at_gui.update_presets(e.player, pdata) 695 | end, 696 | textfield = function(e) 697 | e.element.select_all() 698 | end 699 | } 700 | at_gui.handlers.sliders = { 701 | request = function(e) 702 | local pdata = e.pdata 703 | if not pdata.selected then return end 704 | at_gui.update_request_config(e.player, e.element.slider_value, pdata) 705 | end, 706 | request_text = function(e) 707 | local pdata = e.pdata 708 | if not pdata.selected then return end 709 | at_gui.update_request_config(e.player, tonumber_max(e.element.text), pdata, true) 710 | end, 711 | trash = function(e) 712 | local pdata = e.pdata 713 | if not pdata.selected then return end 714 | at_gui.update_trash_config(e.player, pdata, e.element.slider_value, "slider") 715 | end, 716 | trash_text = function(e) 717 | local pdata = e.pdata 718 | if not pdata.selected then return end 719 | at_gui.update_trash_config(e.player, pdata, tonumber_max(e.element.text), "text") 720 | end, 721 | trash_confirmed = function(e) 722 | local pdata = e.pdata 723 | if not pdata.selected then return end 724 | at_gui.update_trash_config(e.player, pdata, tonumber_max(e.element.text), "confirmed") 725 | end 726 | } 727 | 728 | at_gui.handlers.settings = { 729 | toggle = function(e) 730 | local pdata = e.pdata 731 | local player = e.player 732 | if not player.character then return end 733 | local name = e.element.name 734 | if at_gui.toggle_setting[name] then 735 | if player_data.import_when_empty(player, pdata) then 736 | at_gui.adjust_slots(pdata) 737 | at_gui.update_sliders(pdata) 738 | at_gui.update_buttons(pdata) 739 | end 740 | pdata.flags[name] = e.element.state 741 | e.element.state = at_gui.toggle_setting[name](e.player, pdata) 742 | elseif pdata.flags[name] ~= nil then 743 | pdata.flags[name] = e.element.state 744 | end 745 | end, 746 | toggle_status_display = function(e) 747 | e.element.state = at_gui.toggle_status_display(e.player, e.pdata) 748 | end, 749 | add_network = function(e) 750 | local player = e.player 751 | if not player.character then return end 752 | local pdata = e.pdata 753 | local new_network = at_util.get_network_entity(player) 754 | if new_network then 755 | local new_id = new_network.unit_number 756 | if pdata.networks[new_id] then 757 | player.print{"at-message.network-exists", new_id} 758 | return 759 | end 760 | local new_net = new_network.logistic_network 761 | for id, network in pairs(pdata.networks) do 762 | if network and network.valid then 763 | if network.logistic_network == new_net then 764 | player.print{"at-message.network-exists", id} 765 | return 766 | end 767 | else 768 | pdata.networks[id] = nil 769 | end 770 | end 771 | pdata.networks[new_id] = new_network 772 | player.print{"at-message.added-network", new_id} 773 | else 774 | player.print{"at-message.not-in-network"} 775 | end 776 | at_gui.update_networks(pdata) 777 | end, 778 | remove_network = function(e) 779 | local player = e.player 780 | if not player.character then return end 781 | local pdata = e.pdata 782 | local current_network = at_util.get_network_entity(player) 783 | if current_network then 784 | local nid = current_network.unit_number 785 | if pdata.networks[nid] then 786 | pdata.networks[nid] = nil 787 | player.print{"at-message.removed-network", nid} 788 | at_gui.update_networks(pdata) 789 | return 790 | end 791 | local new_net = current_network.logistic_network 792 | for id, network in pairs(pdata.networks) do 793 | if network and network.valid then 794 | if network.logistic_network == new_net then 795 | pdata.networks[id] = nil 796 | player.print{"at-message.removed-network", id} 797 | return 798 | end 799 | else 800 | pdata.networks[id] = nil 801 | end 802 | end 803 | else 804 | player.print{"at-message.not-in-network"} 805 | end 806 | at_gui.update_networks(pdata) 807 | end, 808 | edit_networks = function(e) 809 | local pdata = e.pdata 810 | at_gui.update_networks(pdata) 811 | local visible = not pdata.gui.networks.window.visible 812 | e.element.style = visible and "at_selected_tool_button" or "tool_button" 813 | pdata.gui.networks.window.visible = visible 814 | pdata.gui.presets.window.visible = not visible 815 | end, 816 | selection_tool = function(e) 817 | local player = e.player 818 | local pdata = e.pdata 819 | local cursor_stack = player.cursor_stack 820 | if cursor_stack and cursor_stack.valid_for_read then 821 | player.clear_cursor() 822 | end 823 | if cursor_stack.set_stack{name = "autotrash-network-selection", count = 1} then 824 | local location = pdata.gui.main.window.location 825 | location.x = 50 826 | pdata.gui.main.window.location = location 827 | end 828 | end, 829 | } 830 | at_gui.handlers.networks = { 831 | view = function(e) 832 | local pdata = e.pdata 833 | local id = tonumber(e.element.parent.name) 834 | local entity = pdata.networks[id] 835 | if entity and entity.valid then 836 | e.player.zoom_to_world(entity.position, 0.3) 837 | local location = pdata.gui.main.window.location 838 | location.x = 50 839 | pdata.gui.main.window.location = location 840 | end 841 | end, 842 | remove = function(e) 843 | local pdata = e.pdata 844 | local flow = e.element.parent 845 | local id = tonumber(flow.name) 846 | if id then 847 | pdata.networks[id] = nil 848 | end 849 | flow.destroy() 850 | end 851 | } 852 | at_gui.handlers.import = { 853 | import_button = function(e, msg) 854 | local player = e.player 855 | local pdata = e.pdata 856 | local inventory = game.create_inventory(1) 857 | 858 | inventory.insert{name = "blueprint"} 859 | local stack = inventory[1] 860 | local result = stack.import_stack(pdata.gui.import.textbox.text) 861 | if result ~= 0 then 862 | inventory.destroy() 863 | player.print({"failed-to-import-string", "Unknown error"}) 864 | at_gui.handlers.import.close_button(e) 865 | return result 866 | end 867 | result = import_presets(player, pdata, msg.all, stack) 868 | inventory.destroy() 869 | if not result then 870 | player.print({"failed-to-import-string", "Unknown error"}) 871 | end 872 | at_gui.handlers.import.close_button(e) 873 | end, 874 | close_button = function(e) 875 | local pdata = e.pdata 876 | pdata.gui.import.window.destroy() 877 | pdata.gui.import = nil 878 | end 879 | } 880 | 881 | function at_gui.update_request_config(player, number, pdata, from_text) 882 | local selected = pdata.selected 883 | local config_tmp = pdata.config_tmp 884 | local item_config = config_tmp.config[selected] 885 | if not item_config then 886 | pdata.selected = false 887 | at_gui.update_button_styles(player, pdata) 888 | at_gui.update_sliders(pdata) 889 | return 890 | end 891 | if not from_text then 892 | number = number * item_prototype(item_config.name).stack_size 893 | end 894 | if item_config.min == 0 and number > 0 then 895 | config_tmp.c_requests = config_tmp.c_requests + 1 896 | end 897 | if item_config.min > 0 and number == 0 then 898 | config_tmp.c_requests = config_tmp.c_requests > 0 and config_tmp.c_requests - 1 or 0 899 | end 900 | item_config.min = number 901 | --prevent trash being set to a lower value than request to prevent infinite robo loop 902 | if item_config.max < constants.max_request and number > item_config.max then 903 | item_config.max = number 904 | end 905 | at_gui.mark_dirty(player, pdata) 906 | at_gui.update_sliders(pdata) 907 | at_gui.update_button(pdata, pdata.selected) 908 | end 909 | 910 | function at_gui.update_trash_config(player, pdata, number, source) 911 | local selected = pdata.selected 912 | local config_tmp = pdata.config_tmp 913 | local item_config = config_tmp.config[selected] 914 | if item_config then 915 | if source == "slider" then 916 | local stack_size = item_prototype(item_config.name).stack_size 917 | number = number < 10 and number * stack_size or constants.max_request 918 | end 919 | if source ~= "text" then 920 | if number < constants.max_request and item_config.min > number then 921 | if item_config.min > 0 and number == 0 then 922 | config_tmp.c_requests = config_tmp.c_requests > 0 and config_tmp.c_requests - 1 or 0 923 | end 924 | item_config.min = number 925 | if source == "confirmed" then 926 | player.print{"at-message.adjusted-trash-amount", at_util.item_prototype(item_config.name).localised_name, number} 927 | end 928 | end 929 | end 930 | item_config.max = number 931 | at_gui.mark_dirty(player, pdata) 932 | at_gui.update_button(pdata, pdata.selected) 933 | else 934 | at_gui.update_button(pdata, pdata.selected) 935 | pdata.selected = false 936 | end 937 | at_gui.update_sliders(pdata) 938 | end 939 | 940 | function at_gui.adjust_slots(pdata, scroll_target) 941 | local slot_table = pdata.gui.main.slot_table 942 | local old_slots = #slot_table.children 943 | local columns = pdata.settings.columns 944 | local slots = math.ceil(pdata.config_tmp.max_slot / columns) * columns 945 | --increase if anything is set in the last row 946 | if (slots == pdata.config_tmp.max_slot) or (pdata.config_tmp.max_slot % columns > 0) then 947 | slots = slots + columns 948 | end 949 | slots = clamp(slots, 4 * columns, 1000) 950 | if old_slots == slots then return end 951 | 952 | local diff = slots - old_slots 953 | if diff > 0 then 954 | for i = old_slots+1, slots do 955 | gui.build(slot_table, {at_gui.templates.slot_table.button(i, pdata)}) 956 | end 957 | elseif diff < 0 then 958 | for i = old_slots, slots+1, -1 do 959 | local btn = slot_table.children[i] 960 | btn.destroy() 961 | end 962 | end 963 | local target = scroll_target and slot_table.children[scroll_target] or slot_table.children[slots] 964 | pdata.gui.main.config_rows.scroll_to_element(target) 965 | end 966 | 967 | function at_gui.update_buttons(pdata) 968 | if not pdata.flags.gui_open then return end 969 | local children = pdata.gui.main.slot_table.children 970 | for i=1, #children do 971 | at_gui.update_button(pdata, i, children[i]) 972 | end 973 | end 974 | 975 | function at_gui.get_button_style(i, selected, item, on_the_way, item_count, cursor_stack, armor, gun, ammo, paused) 976 | if paused or not (on_the_way and item and item.min > 0) then 977 | return (i == selected) and "yellow_slot_button" or "slot_button" 978 | end 979 | if i == selected then 980 | return "yellow_slot_button" 981 | end 982 | local n = item.name 983 | local diff = item.min - ((item_count[n] or 0) + (armor[n] or 0) + (gun[n] or 0) + (ammo[n] or 0) + (cursor_stack[n] or 0)) 984 | 985 | if diff <= 0 then 986 | return "slot_button" 987 | else 988 | if on_the_way[n] then 989 | return "yellow_slot_button", diff 990 | end 991 | return "red_slot_button", diff 992 | end 993 | end 994 | 995 | function at_gui.update_button_styles(player, pdata) 996 | local ruleset_grid = pdata.gui.main.slot_table 997 | if not (ruleset_grid and ruleset_grid.valid) then return end 998 | local selected = pdata.selected 999 | local config = pdata.config_tmp 1000 | local network, on_the_way, item_count, cursor_stack, armor, gun, ammo = get_network_data(player) 1001 | if not (network and on_the_way and config.c_requests > 0 and not pdata.flags.pause_requests) then 1002 | local children = ruleset_grid.children 1003 | for i=1, #children do 1004 | children[i].style = (i == selected) and "yellow_slot_button" or "slot_button" 1005 | end 1006 | return 1007 | end 1008 | config = config.config 1009 | local ret = {} 1010 | local buttons = ruleset_grid.children 1011 | for i=1, #buttons do 1012 | local item = config[i] 1013 | local style, diff = at_gui.get_button_style(i, selected, config[i], on_the_way, item_count, cursor_stack, armor, gun, ammo) 1014 | if item and item.min > 0 then 1015 | ret[item.name] = {style, diff} 1016 | end 1017 | buttons[i].style = style 1018 | end 1019 | return ret 1020 | end 1021 | 1022 | function at_gui.update_button(pdata, i, button) 1023 | if not (button and button.valid) then 1024 | if not i then return end 1025 | button = pdata.gui.main.slot_table.children[i] 1026 | if not button then return end 1027 | end 1028 | local req = pdata.config_tmp.config[i] 1029 | if req then 1030 | if req.min == req.max then 1031 | button.children[1].caption = "" 1032 | button.children[2].caption = format_number(format_request(req), true) 1033 | else 1034 | button.children[1].caption = format_number(format_request(req), true) 1035 | button.children[2].caption = format_trash(req) 1036 | end 1037 | button.elem_value = req.name 1038 | button.locked = i ~= pdata.selected 1039 | else 1040 | button.children[1].caption = "" 1041 | button.children[2].caption = "" 1042 | button.elem_value = nil 1043 | button.locked = false 1044 | end 1045 | button.style = (i == pdata.selected) and "yellow_slot_button" or "slot_button" 1046 | end 1047 | 1048 | function at_gui.clear_button(player, pdata, index, button) 1049 | player_data.clear_config(pdata, index) 1050 | at_gui.mark_dirty(player, pdata) 1051 | at_gui.update_button(pdata, index, button) 1052 | at_gui.update_sliders(pdata) 1053 | end 1054 | 1055 | function at_gui.update_sliders(pdata) 1056 | if not pdata.flags.gui_open then return end 1057 | local visible = pdata.selected and true or false 1058 | local sliders = pdata.gui.sliders 1059 | if visible then 1060 | local item_config = pdata.config_tmp.config[pdata.selected] 1061 | if item_config then 1062 | local stack_size = item_prototype(item_config.name).stack_size 1063 | sliders.request.slider_value = clamp(item_config.min / stack_size, 0, 10) 1064 | sliders.request_text.text = tostring(format_request(item_config) or "0") 1065 | 1066 | sliders.trash.slider_value = clamp(item_config.max / stack_size, 0, 10) 1067 | sliders.trash_text.text = tostring(item_config.max < constants.max_request and item_config.max or "inf.") 1068 | end 1069 | end 1070 | sliders.table.visible = visible 1071 | end 1072 | 1073 | function at_gui.update_presets(player, pdata) 1074 | spider_gui.update(player, pdata) 1075 | if not pdata.flags.gui_open then return end 1076 | local children = pdata.gui.presets.scroll.children 1077 | local selected_presets = pdata.selected_presets 1078 | local death_presets = pdata.death_presets 1079 | for i=1, #children do 1080 | local preset = children[i].children[1] 1081 | local rip = children[i].children[2] 1082 | local preset_name = preset.caption 1083 | preset.style = selected_presets[preset_name] and "at_preset_button_selected" or "at_preset_button" 1084 | rip.style = death_presets[preset_name] and "at_preset_button_small_selected" or "at_preset_button_small" 1085 | end 1086 | local s = table_size(selected_presets) 1087 | if s == 1 then 1088 | pdata.gui.presets.textfield.text = next(selected_presets) 1089 | elseif s > 1 then 1090 | pdata.gui.presets.textfield.text = "" 1091 | end 1092 | end 1093 | 1094 | function at_gui.update_networks(pdata) 1095 | if not pdata.flags.gui_open then return end 1096 | local networks = pdata.gui.networks.scroll 1097 | networks.clear() 1098 | gui.build(networks, at_gui.templates.networks(pdata)) 1099 | end 1100 | 1101 | function at_gui.adjust_size(pdata) 1102 | local slot_table = pdata.gui.main.slot_table 1103 | if slot_table.column_count ~= pdata.settings.columns then 1104 | local scroll = pdata.gui.main.config_rows 1105 | scroll.clear() 1106 | local refs = gui.build(scroll, {at_gui.templates.slot_table.main(40, pdata)}) 1107 | pdata.gui.main.slot_table = refs.main.slot_table 1108 | end 1109 | at_gui.adjust_slots(pdata) 1110 | end 1111 | 1112 | function at_gui.create_main_window(player, pdata) 1113 | if not player.character then return end 1114 | local flags = pdata.flags 1115 | pdata.selected = false 1116 | local btns = math.max(40, player.character.request_slot_count, pdata.config_tmp.max_slot) 1117 | local resolution = player.display_resolution 1118 | local scale = player.display_scale 1119 | local pin_sprite = flags.pinned and "at_pin_black" or "at_pin_white" 1120 | local gui_dimensions = constants.gui_dimensions 1121 | local gui_data = gui.build(player.gui.screen,{ 1122 | {type = "frame", 1123 | style_mods = { 1124 | maximal_width = (resolution.width / scale), 1125 | maximal_height = (resolution.height / scale) * 0.97, 1126 | height = pdata.settings.rows * 40 + gui_dimensions.window, 1127 | }, 1128 | direction = "vertical", 1129 | actions = {on_closed = {gui = "main", action = "window"}}, 1130 | ref = {"main", "window"}, 1131 | children = { 1132 | {type = "flow", ref = {"main", "titlebar"}, children = { 1133 | {type = "label", style = "frame_title", caption = {"mod-name.AutoTrash"}, elem_mods = {ignored_by_interaction = true}}, 1134 | {type = "empty-widget", style = "flib_titlebar_drag_handle", elem_mods = {ignored_by_interaction = true}}, 1135 | gui_util.frame_action_button(pin_sprite, "at_pin_black", 1136 | {gui = "main", action = "pin_button"}, 1137 | {"main", "pin_button"}, {tooltip={"at-gui.keep-open"}} 1138 | ), 1139 | gui_util.frame_action_button("utility/close_white", "utility/close_black", 1140 | {gui = "main", action = "close_button"} 1141 | ) 1142 | }}, 1143 | {type = "flow", direction = "horizontal", style = "inset_frame_container_horizontal_flow", children = { 1144 | {type = "frame", style = "inside_shallow_frame", direction = "vertical", children = { 1145 | {type = "frame", style = "subheader_frame", children={ 1146 | {type = "label", style = "subheader_caption_label", caption = {"at-gui.logistics-configuration"}}, 1147 | gui_util.pushers.horizontal, 1148 | {type = "sprite-button", style = "item_and_count_select_confirm", 1149 | sprite = "utility/check_mark", tooltip = {"module-inserter-config-button-apply"}, 1150 | actions = {on_click = {gui = "main", action = "apply_changes"}}, 1151 | }, 1152 | {type = "sprite-button", style = "tool_button_red", ref = {"main", "reset_button"}, sprite = "utility/reset", 1153 | actions = {on_click = {gui = "main", action = "reset"}}, 1154 | }, 1155 | {type = "sprite-button", style = "tool_button", sprite = "utility/export_slot", tooltip = {"at-gui.tooltip-export"}, 1156 | actions = {on_click = {gui = "main", action = "export"}} 1157 | }, 1158 | {type = "sprite-button", style = "tool_button", sprite = "at_import_string", tooltip = {"at-gui.tooltip-import"}, 1159 | actions = {on_click = {gui = "main", action = "import"}}, 1160 | } 1161 | }}, 1162 | {type = "flow", direction="vertical", style_mods = {padding= 12, top_padding = 8, vertical_spacing = 10}, children = { 1163 | {type = "frame", style = "deep_frame_in_shallow_frame", children = { 1164 | {type = "scroll-pane", style = "at_slot_table_scroll_pane", ref = {"main", "config_rows"}, 1165 | children = { 1166 | at_gui.templates.slot_table.main(btns, pdata), 1167 | } 1168 | } 1169 | }}, 1170 | {type = "frame", style = "at_bordered_frame2", direction = "vertical", children = { 1171 | {type = "table", ref = {"sliders", "table"}, style_mods = {horizontal_spacing = 8}, column_count = 3, children = { 1172 | {type = "label", caption = {"at-gui.request"}}, 1173 | {type = "slider", ref = {"sliders", "request"}, 1174 | minimum_value = 0, maximum_value = 10, style = "notched_slider", 1175 | actions = {on_value_changed = {gui = "sliders", action = "request"}} 1176 | }, 1177 | {type = "textfield", style = "slider_value_textfield", 1178 | numeric = true, allow_negative = false, lose_focus_on_confirm = true, 1179 | ref = {"sliders", "request_text"}, 1180 | actions = {on_text_changed = {gui = "sliders", action = "request_text"}}, 1181 | }, 1182 | {type = "label", caption={"at-gui.trash"}}, 1183 | {type = "slider", ref = {"sliders", "trash"}, style = "notched_slider", 1184 | minimum_value = 0, maximum_value = 10, 1185 | actions = {on_value_changed = {gui = "sliders", action = "trash"}}, 1186 | }, 1187 | {type = "textfield", style = "slider_value_textfield", 1188 | numeric = true, allow_negative = false, lose_focus_on_confirm = true, 1189 | ref = {"sliders", "trash_text"}, 1190 | actions = { 1191 | on_text_changed = {gui = "sliders", action = "trash_text"}, 1192 | on_confirmed = {gui = "sliders", action = "trash_confirmed"} 1193 | }, 1194 | }, 1195 | }}, 1196 | {type = "drop-down", style = "at_quick_actions", tooltip = {"at-gui.tooltip-quick-actions"}, 1197 | actions = {on_selection_state_changed = {gui = "main", action = "quick_actions"}}, 1198 | items = constants.quick_actions, 1199 | selected_index = 1, 1200 | }, 1201 | }}, 1202 | {type = "frame", style = "at_bordered_frame", direction = "vertical", ref = {"options", "window"}, 1203 | children = at_gui.templates.options(flags), 1204 | } 1205 | }}, 1206 | 1207 | }}, 1208 | {type = "frame", ref = {"presets", "window"}, style = "inside_shallow_frame", direction = "vertical", children = { 1209 | {type = "frame", style = "subheader_frame", children={ 1210 | {type = "label", style = "subheader_caption_label", caption = {"at-gui.presets"}}, 1211 | gui_util.pushers.horizontal, 1212 | {type = "sprite-button", style = "tool_button", sprite = "utility/export_slot", tooltip = {"at-gui.tooltip-export-all"}, 1213 | actions = {on_click = {gui = "main", action = "export_all"}}, 1214 | }, 1215 | {type = "sprite-button", style = "tool_button", sprite = "at_import_string", tooltip = {"at-gui.tooltip-import-all"}, 1216 | actions = {on_click = {gui = "main", action = "import_all"}}, 1217 | }, 1218 | }}, 1219 | {type = "flow", direction="vertical", style = "at_right_container_flow", children = { 1220 | {type = "flow", children = { 1221 | {type = "textfield", style = "long_number_textfield", ref = {"presets", "textfield"}, 1222 | actions = {on_click = {gui = "presets", action = "textfield"}}, 1223 | }, 1224 | gui_util.pushers.horizontal, 1225 | {type = "button", caption = {"gui-save-game.save"}, style = "at_save_button", 1226 | actions = {on_click = {gui = "presets", action = "save"}} 1227 | } 1228 | }}, 1229 | {type = "frame", style = "deep_frame_in_shallow_frame", children = { 1230 | {type = "scroll-pane", style = "at_right_scroll_pane", ref = {"presets", "scroll"}, 1231 | children = gui_util.presets(pdata) 1232 | } 1233 | }}, 1234 | }} 1235 | }}, 1236 | {type = "frame", ref = {"networks", "window"}, visible = false, style = "inside_shallow_frame", direction = "vertical", children = { 1237 | {type = "frame", style = "subheader_frame", children={ 1238 | {type = "label", style = "subheader_caption_label", caption = {"gui-logistic.logistic-networks"}}, 1239 | gui_util.pushers.horizontal, 1240 | {type = "sprite-button", style = "tool_button", style_mods = {padding = 0}, 1241 | actions = {on_click = {gui = "settings", action = "selection_tool"}}, 1242 | sprite = "autotrash_selection", tooltip = {"at-gui.tooltip-selection-tool"} 1243 | }, 1244 | }}, 1245 | {type = "flow", direction = "vertical", style = "at_right_container_flow", children = { 1246 | {type = "frame", style = "deep_frame_in_shallow_frame", children = { 1247 | {type = "scroll-pane", style = "at_right_scroll_pane", ref = {"networks", "scroll"}, 1248 | children = at_gui.templates.networks(pdata) 1249 | } 1250 | }}, 1251 | }} 1252 | }} 1253 | }} 1254 | } 1255 | }, 1256 | }) 1257 | gui_data.main.titlebar.drag_target = gui_data.main.window 1258 | gui_data.main.window.force_auto_center() 1259 | gui_data.main.window.visible = false 1260 | 1261 | pdata.gui.main = gui_data.main 1262 | pdata.gui.sliders = gui_data.sliders 1263 | pdata.gui.presets = gui_data.presets 1264 | pdata.gui.networks = gui_data.networks 1265 | pdata.gui.options = gui_data.options 1266 | if pdata.flags.pinned then 1267 | pdata.gui.main.pin_button.style = "flib_selected_frame_action_button" 1268 | end 1269 | pdata.selected = false 1270 | at_gui.adjust_slots(pdata) 1271 | end 1272 | 1273 | function at_gui.create_import_window(player, pdata, bp_string, all) 1274 | if pdata.gui.import and pdata.gui.import.window and pdata.gui.import.window.valid then 1275 | local window = pdata.gui.import.window 1276 | window.destroy() 1277 | pdata.gui.import = nil 1278 | end 1279 | local caption = bp_string and {"gui.export-to-string"} or {"gui-blueprint-library.import-string"} 1280 | local button_caption = bp_string and {"gui.close"} or {"gui-blueprint-library.import"} 1281 | local button_handler = bp_string and "close_button" or "import_button" 1282 | 1283 | local refs = gui.build(player.gui.screen, { 1284 | {type = "frame", ref = {"window"}, style = "inner_frame_in_outer_frame", direction = "vertical", children = { 1285 | {type = "flow", ref = {"titlebar"}, children = { 1286 | {type = "label", style = "frame_title", caption = caption, elem_mods = {ignored_by_interaction = true}}, 1287 | {type = "empty-widget", style = "flib_titlebar_drag_handle", elem_mods = {ignored_by_interaction = true}}, 1288 | gui_util.frame_action_button("utility/close_white", "utility/close_black", 1289 | {gui = "import", action = "close_button"} 1290 | ) 1291 | }}, 1292 | {type = "text-box", text = bp_string, ref = {"textbox"}, elem_mods = {word_wrap = true}, style_mods = {width = 400, height = 250}}, 1293 | {type = "flow", direction = "horizontal", children={ 1294 | gui_util.pushers.horizontal, 1295 | {type = "button", style = "dialog_button", caption = button_caption, 1296 | actions = {on_click = {gui = "import", action = button_handler, all = all}} 1297 | } 1298 | }} 1299 | }} 1300 | }) 1301 | pdata.gui.import = refs 1302 | local import_window = pdata.gui.import 1303 | import_window.titlebar.drag_target = pdata.gui.import.window 1304 | import_window.window.force_auto_center() 1305 | local textbox = import_window.textbox 1306 | if bp_string then 1307 | textbox.read_only = true 1308 | end 1309 | textbox.select_all() 1310 | textbox.focus() 1311 | end 1312 | 1313 | 1314 | function at_gui.init(player, pdata) 1315 | at_gui.destroy(player, pdata) 1316 | at_gui.update_main_button(player, pdata) 1317 | at_gui.init_status_display(player, pdata) 1318 | spider_gui.init(player, pdata) 1319 | end 1320 | 1321 | function at_gui.init_main_button(player, pdata, destroy) 1322 | local flow = mod_gui.get_button_flow(player) 1323 | local visible = pdata.flags.can_open_gui and pdata.settings.show_button 1324 | local button = flow.at_config_button 1325 | button = (button and button.valid) and button 1326 | if destroy and button then 1327 | button.destroy() 1328 | button = nil 1329 | end 1330 | if visible then 1331 | if not button then 1332 | local children = #flow.children 1333 | local index = pdata.main_button_index 1334 | if index and index > children then 1335 | index = nil 1336 | end 1337 | local gui_data = gui.build(flow, {{type = "sprite-button", name = "at_config_button", style = mod_gui.button_style, 1338 | actions = {on_click = {gui = "main", action = "mod_gui_button"}}, 1339 | ref = {"main_button"}, 1340 | index = index, 1341 | sprite = "autotrash_trash", tooltip = {"at-gui.tooltip-main-button", pdata.flags.status_display_open and "On" or "Off"} 1342 | }}) 1343 | pdata.gui.main_button = gui_data.main_button 1344 | else 1345 | button.visible = true 1346 | pdata.gui.main_button = button 1347 | pdata.main_button_index = button.get_index_in_parent() 1348 | gui.update_tags(button, {flib = {on_click = {gui = "main", action = "mod_gui_button"}}}) 1349 | end 1350 | return button 1351 | else 1352 | if button then 1353 | pdata.main_button_index = button.get_index_in_parent() 1354 | button.visible = false 1355 | end 1356 | end 1357 | end 1358 | 1359 | function at_gui.init_status_display(player, pdata, keep_status) 1360 | local status_flow = pdata.gui.status_flow 1361 | if not (status_flow and status_flow.valid) then 1362 | status_flow = mod_gui.get_frame_flow(player).autotrash_status_flow 1363 | if not (status_flow and status_flow.valid) then 1364 | status_flow = mod_gui.get_frame_flow(player).add{type = "flow", name = "autotrash_status_flow", direction = "vertical"} 1365 | end 1366 | pdata.gui.status_flow = status_flow 1367 | end 1368 | status_flow.clear() 1369 | 1370 | local visible = false 1371 | if keep_status then 1372 | visible = pdata.flags.can_open_gui and pdata.flags.status_display_open 1373 | end 1374 | status_flow.visible = visible 1375 | pdata.flags.status_display_open = visible 1376 | pdata.gui.status_table = nil 1377 | 1378 | local status_table = status_flow.add{ 1379 | type = "table", 1380 | style = "at_request_status_table", 1381 | column_count = pdata.settings.status_columns 1382 | } 1383 | pdata.gui.status_table = status_table 1384 | 1385 | for _ = 1, pdata.settings.status_count do 1386 | status_table.add{ 1387 | type = "sprite-button", 1388 | visible = false 1389 | } 1390 | end 1391 | at_gui.update_options(pdata) 1392 | at_gui.update_status_display(player, pdata) 1393 | return status_table 1394 | end 1395 | 1396 | function at_gui.open_status_display(player, pdata) 1397 | local status_table = pdata.gui.status_table 1398 | if not (status_table and status_table.valid) then 1399 | status_table = at_gui.init_status_display(player, pdata) 1400 | end 1401 | if pdata.flags.can_open_gui then 1402 | status_table.parent.visible = true 1403 | pdata.flags.status_display_open = true 1404 | at_gui.update_main_button(player, pdata) 1405 | at_gui.update_status_display(player, pdata) 1406 | end 1407 | at_gui.update_options(pdata) 1408 | end 1409 | 1410 | function at_gui.close_status_display(player, pdata) 1411 | pdata.flags.status_display_open = false 1412 | at_gui.update_options(pdata) 1413 | at_gui.update_main_button(player, pdata) 1414 | local status_table = pdata.gui.status_table 1415 | if not (status_table and status_table.valid) then 1416 | return 1417 | end 1418 | status_table.parent.visible = false 1419 | end 1420 | 1421 | function at_gui.toggle_status_display(player, pdata) 1422 | if pdata.flags.status_display_open then 1423 | at_gui.close_status_display(player, pdata) 1424 | return false 1425 | else 1426 | at_gui.open_status_display(player, pdata) 1427 | return true 1428 | end 1429 | end 1430 | 1431 | function at_gui.update_status_display(player, pdata) 1432 | if not pdata.flags.status_display_open then return end 1433 | local status_table = pdata.gui.status_table 1434 | if not (status_table and status_table.valid) then 1435 | at_gui.init_status_display(player, pdata) 1436 | return --init already updates it 1437 | end 1438 | local network, on_the_way, item_count, cursor_stack, armor, gun, ammo = get_network_data(player) 1439 | if not (network and not pdata.flags.pause_requests) then 1440 | for _, child in pairs(status_table.children) do 1441 | child.visible = false 1442 | end 1443 | return true 1444 | end 1445 | 1446 | local max_count = pdata.settings.status_count 1447 | local get_request_slot = player.character.get_request_slot 1448 | 1449 | local children = status_table.children 1450 | local c = 1 1451 | for i = 1, player.character.request_slot_count do 1452 | local item = get_request_slot(i) 1453 | if item and item.count > 0 then 1454 | if c > max_count then return true end 1455 | item.min = item.count 1456 | if item.min > 0 then 1457 | local style, diff = at_gui.get_button_style(i, false, item, on_the_way, item_count, cursor_stack, armor, gun, ammo) 1458 | if style ~= "slot_button" then 1459 | local button = children[c] 1460 | button.style = style 1461 | button.sprite = "item/" .. item.name 1462 | button.number = diff 1463 | button.visible = true 1464 | c = c + 1 1465 | end 1466 | end 1467 | end 1468 | end 1469 | for i = c, max_count do 1470 | children[i].visible = false 1471 | end 1472 | return true 1473 | end 1474 | 1475 | function at_gui.update_main_button(player, pdata) 1476 | local mainButton = at_gui.init_main_button(player, pdata) 1477 | if not mainButton then return end 1478 | local flags = pdata.flags 1479 | if flags.pause_trash and not flags.pause_requests then 1480 | mainButton.sprite = "autotrash_trash_paused" 1481 | elseif flags.pause_requests and not flags.pause_trash then 1482 | mainButton.sprite = "autotrash_requests_paused" 1483 | elseif flags.pause_trash and flags.pause_requests then 1484 | mainButton.sprite = "autotrash_both_paused" 1485 | else 1486 | mainButton.sprite = "autotrash_trash" 1487 | end 1488 | mainButton.tooltip = {"at-gui.tooltip-main-button", flags.status_display_open and "On" or "Off"} 1489 | at_gui.update_options(pdata) 1490 | end 1491 | 1492 | function at_gui.update_options(pdata) 1493 | if not pdata.flags.gui_open then return end 1494 | local frame = pdata.gui.options.window 1495 | if not (frame and frame.valid) then return end 1496 | local flags = pdata.flags 1497 | local def = at_gui.defines 1498 | 1499 | pdata.gui.options.trash_unrequested.state = flags.trash_unrequested 1500 | pdata.gui.options.autotoggle_unrequested = flags.autotoggle_unrequested 1501 | frame[def.trash_above_requested].state = flags.trash_above_requested 1502 | frame[def.trash_network].state = flags.trash_network 1503 | frame[def.pause_trash].state = flags.pause_trash 1504 | frame[def.pause_requests].state = flags.pause_requests 1505 | frame[def.status_display].state = flags.status_display_open 1506 | end 1507 | 1508 | function at_gui.mark_dirty(player, pdata, keep_presets) 1509 | local reset = pdata.gui.main.reset_button 1510 | reset.enabled = true 1511 | pdata.flags.dirty = true 1512 | if not keep_presets then 1513 | pdata.selected_presets = {} 1514 | end 1515 | at_gui.update_presets(player, pdata) 1516 | end 1517 | 1518 | function at_gui.destroy(player, pdata) 1519 | if pdata.gui.main and pdata.gui.main.window and pdata.gui.main.window.valid then 1520 | pdata.gui.main.window.destroy() 1521 | end 1522 | pdata.selected = false 1523 | pdata.gui.main = {} 1524 | pdata.gui.sliders = {} 1525 | pdata.gui.options = {} 1526 | pdata.gui.presets = {} 1527 | pdata.gui.networks = {} 1528 | pdata.flags.gui_open = false 1529 | if pdata.gui.import and pdata.gui.import.window and pdata.gui.import.window.valid then 1530 | pdata.gui.import.window.destroy() 1531 | end 1532 | pdata.gui.import = {} 1533 | player.set_shortcut_toggled("autotrash-toggle-gui", false) 1534 | end 1535 | 1536 | function at_gui.open(player, pdata) 1537 | if not pdata.flags.can_open_gui then return end 1538 | if not player.character then 1539 | player.print{"at-message.no-character"} 1540 | at_gui.close(player, pdata, true) 1541 | return 1542 | end 1543 | local window_frame = pdata.gui.main.window 1544 | if not (window_frame and window_frame.valid) then 1545 | if window_frame then 1546 | player.print{"at-message.invalid-gui"} 1547 | end 1548 | at_gui.destroy(player, pdata) 1549 | at_gui.create_main_window(player, pdata) 1550 | window_frame = pdata.gui.main.window 1551 | end 1552 | window_frame.visible = true 1553 | window_frame.bring_to_front() 1554 | pdata.flags.gui_open = true 1555 | if not pdata.flags.pinned then 1556 | player.opened = window_frame 1557 | end 1558 | player.set_shortcut_toggled("autotrash-toggle-gui", true) 1559 | 1560 | at_gui.adjust_slots(pdata) 1561 | at_gui.update_buttons(pdata) 1562 | at_gui.update_button_styles(player, pdata) 1563 | at_gui.update_options(pdata) 1564 | at_gui.update_sliders(pdata) 1565 | at_gui.update_presets(player, pdata) 1566 | end 1567 | 1568 | function at_gui.close(player, pdata, no_reset) 1569 | if pdata.closing then return end--no need to do it twice if not pinned and the close button is used 1570 | local window_frame = pdata.gui.main.window 1571 | if not (window_frame and window_frame.valid) then 1572 | if window_frame then 1573 | player.print{"at-message.invalid-gui"} 1574 | end 1575 | at_gui.destroy(player, pdata) 1576 | at_gui.create_main_window(player, pdata) 1577 | window_frame = pdata.gui.main.window 1578 | end 1579 | if window_frame and window_frame.valid then 1580 | window_frame.visible = false 1581 | end 1582 | pdata.flags.gui_open = false 1583 | pdata.selected = false 1584 | if player.opened == window_frame then 1585 | pdata.closing = true 1586 | player.opened = nil 1587 | pdata.closing = nil 1588 | end 1589 | if pdata.gui.networks.window and pdata.gui.presets.window then 1590 | pdata.gui.networks.window.visible = false 1591 | pdata.gui.presets.window.visible = true 1592 | pdata.gui.main.network_edit_button.style = "tool_button" 1593 | end 1594 | if not no_reset and pdata.settings.reset_on_close then 1595 | pdata.config_tmp = at_util.copy_preset(pdata.config_new) 1596 | pdata.gui.main.reset_button.enabled = false 1597 | pdata.dirty = false 1598 | end 1599 | player.set_shortcut_toggled("autotrash-toggle-gui", false) 1600 | end 1601 | 1602 | function at_gui.recreate(player, pdata, no_spider) 1603 | local was_open = pdata.flags.gui_open 1604 | at_gui.destroy(player, pdata) 1605 | if not no_spider then 1606 | spider_gui.init(player, pdata) 1607 | end 1608 | if was_open then 1609 | at_gui.open(player, pdata) 1610 | else 1611 | player.set_shortcut_toggled("autotrash-toggle-gui", false) 1612 | at_gui.create_main_window(player, pdata) 1613 | end 1614 | end 1615 | 1616 | function at_gui.toggle(player, pdata) 1617 | if pdata.flags.gui_open then 1618 | at_gui.close(player, pdata) 1619 | else 1620 | at_gui.open(player, pdata) 1621 | end 1622 | end 1623 | return at_gui -------------------------------------------------------------------------------- /scripts/migrations.lua: -------------------------------------------------------------------------------- 1 | local global_data = require("scripts.global-data") 2 | local player_data = require("scripts.player-data") 3 | local at_gui = require("scripts.gui") 4 | local spider_gui = require("scripts.spidertron") 5 | local constants = require("constants") 6 | 7 | local mod_gui = require ("__core__.lualib.mod-gui") 8 | local item_prototype = require("scripts.util").item_prototype 9 | 10 | local migrations = { 11 | ["4.1.2"] = function() 12 | log("Resetting all AutoTrash settings") 13 | global = {} 14 | global_data.init() 15 | for player_index in pairs(game.players) do 16 | player_data.init(player_index) 17 | end 18 | end, 19 | ["5.1.0"] = function() 20 | for _, pdata in pairs(global._pdata) do 21 | pdata.infinite = nil 22 | end 23 | end, 24 | ["5.2.2"] = function() 25 | global.unlocked_by_force = {} 26 | end, 27 | ["5.2.3"] = function() 28 | for player_index, player in pairs(game.players) do 29 | local pdata = global._pdata[player_index] 30 | if pdata then 31 | local psettings = pdata.settings 32 | pdata.flags = { 33 | can_open_gui = player.force.character_logistic_requests, 34 | gui_open = false, 35 | status_display_open = false, 36 | trash_above_requested = psettings.trash_above_requested or false, 37 | trash_unrequested = psettings.trash_unrequested or false, 38 | trash_network = psettings.trash_network or false, 39 | pause_trash = psettings.pause_trash or false, 40 | pause_requests = psettings.pause_requests or false, 41 | } 42 | pdata.gui = { 43 | import = {}, 44 | main = {} 45 | } 46 | pdata.presets = pdata.storage_new 47 | if pdata.presets then 48 | for _, stored in pairs(pdata.presets) do 49 | --remove invalid items 50 | for i = stored.max_slot, 1, -1 do 51 | local item_config = stored.config[i] 52 | if item_config then 53 | if not item_prototype(item_config.name) then 54 | if stored.config[i].request > 0 then 55 | stored.c_requests = stored.c_requests - 1 56 | end 57 | stored.config[i] = nil 58 | if stored.max_slot == i then 59 | stored.max_slot = false 60 | end 61 | else 62 | stored.max_slot = stored.max_slot or i 63 | end 64 | end 65 | end 66 | end 67 | else 68 | pdata.presets = {} 69 | end 70 | pdata.storage_new = nil 71 | pdata.gui_actions = nil 72 | pdata.gui_elements = nil 73 | pdata.gui_location = nil 74 | 75 | player_data.update_settings(player, pdata) 76 | else 77 | pdata = player_data.init(player_index) 78 | end 79 | --keep the status flow in gui.left, everything else goes boom (from AutoTrash) 80 | local mod_gui_flow = mod_gui.get_frame_flow(player) 81 | if mod_gui_flow and mod_gui_flow.valid then 82 | for _, egui in pairs(mod_gui_flow.children) do 83 | if egui.get_mod() == "AutoTrash" then 84 | if egui.name == "autotrash_status_flow" then 85 | pdata.gui.status_flow = egui 86 | egui.clear() 87 | else 88 | egui.destroy() 89 | end 90 | end 91 | end 92 | end 93 | local button_flow = mod_gui.get_button_flow(player).autotrash_main_flow 94 | if button_flow and button_flow.valid then 95 | button_flow.destroy() 96 | end 97 | for _, egui in pairs(player.gui.screen.children) do 98 | if egui.get_mod() == "AutoTrash" then 99 | egui.destroy() 100 | end 101 | end 102 | end 103 | 104 | for pi, player in pairs(game.players) do 105 | local pdata = global._pdata[pi] 106 | player_data.refresh(player, pdata) 107 | at_gui.init(player, pdata) 108 | end 109 | end, 110 | ["5.2.4"] = function() 111 | for player_index, player in pairs(game.players) do 112 | local pdata = global._pdata[player_index] 113 | pdata.flags.dirty = false 114 | pdata.dirty = nil 115 | at_gui.init_status_display(player, pdata) 116 | at_gui.open_status_display(player, pdata) 117 | end 118 | end, 119 | ["5.2.9"] = function() 120 | for player_index, _ in pairs(game.players) do 121 | local pdata = global._pdata[player_index] 122 | pdata.flags.pinned = true 123 | end 124 | end, 125 | ["5.2.11"] = function() 126 | local set_trash = function(data) 127 | for _, config in pairs(data.config) do 128 | if not config.trash then 129 | config.trash = constants.max_request 130 | end 131 | config.max = config.trash or constants.max_request 132 | config.min = config.request or 0 133 | config.trash = nil 134 | config.request = nil 135 | end 136 | end 137 | 138 | for _, pdata in pairs(global._pdata) do 139 | set_trash(pdata.config_tmp) 140 | set_trash(pdata.config_new) 141 | for _, preset in pairs(pdata.presets) do 142 | set_trash(preset) 143 | end 144 | pdata.temporary_trash = nil 145 | pdata.temporary_requests = {} 146 | pdata.flags.has_temporary_requests = false 147 | end 148 | script.on_event(defines.events.on_player_trash_inventory_changed, nil) 149 | end, 150 | ["5.2.13"] = function() 151 | for _, pdata in pairs(global._pdata) do 152 | pdata.next_check = nil 153 | end 154 | end, 155 | ["5.2.14"] = function() 156 | for _, pdata in pairs(global._pdata) do 157 | pdata.networks = {} 158 | if pdata.main_network and pdata.main_network.valid then 159 | pdata.networks[pdata.main_network.unit_number] = pdata.main_network 160 | end 161 | pdata.main_network = nil 162 | end 163 | end, 164 | ["5.2.15"] = function() 165 | for pi, player in pairs(game.players) do 166 | local pdata = global._pdata[pi] 167 | player_data.refresh(player, pdata) 168 | at_gui.init(player, pdata) 169 | end 170 | for _, force in pairs(game.forces) do 171 | if force.character_logistic_requests then 172 | global.unlocked_by_force[force.name] = true 173 | end 174 | end 175 | for _, pdata in pairs(global._pdata) do 176 | pdata.config_tmp.by_name = {} 177 | for _, item_config in pairs(pdata.config_tmp.config) do 178 | pdata.config_tmp.by_name[item_config.name] = item_config 179 | end 180 | pdata.config_new.by_name = {} 181 | for _, item_config in pairs(pdata.config_new.config) do 182 | pdata.config_new.by_name[item_config.name] = item_config 183 | end 184 | for _, preset in pairs(pdata.presets) do 185 | preset.by_name = {} 186 | for _, item_config in pairs(preset.config) do 187 | preset.by_name[item_config.name] = item_config 188 | end 189 | end 190 | end 191 | end, 192 | ["5.2.16"] = function() 193 | for pi, pdata in pairs(global._pdata) do 194 | local player = game.get_player(pi) 195 | player_data.refresh(player, pdata) 196 | if pdata.gui.mod_gui and pdata.gui.mod_gui.flow and pdata.gui.mod_gui.flow.valid then 197 | pdata.gui.mod_gui.flow.destroy() 198 | pdata.gui.mod_gui = {} 199 | end 200 | end 201 | end, 202 | ["5.3.1"] = function() 203 | for pi, pdata in pairs(global._pdata) do 204 | if pdata.gui.mod_gui and pdata.gui.mod_gui.flow and pdata.gui.mod_gui.flow.valid then 205 | local player = game.get_player(pi) 206 | pdata.main_button_index = pdata.gui.mod_gui.flow.get_index_in_parent() 207 | player_data.refresh(player, pdata) 208 | pdata.gui.mod_gui.flow.destroy() 209 | at_gui.update_main_button(player, pdata) 210 | pdata.gui.mod_gui = {} 211 | end 212 | end 213 | end, 214 | ["5.3.2"] = function () 215 | for pi, pdata in pairs(global._pdata) do 216 | local player = game.get_player(pi) 217 | local button_flow = mod_gui.get_button_flow(player) 218 | local at_flow = button_flow.autotrash_main_flow 219 | if at_flow then 220 | at_flow.destroy() 221 | end 222 | pdata.gui.mod_gui = nil 223 | at_gui.update_main_button(player, pdata) 224 | spider_gui.init(player, pdata) 225 | end 226 | global.__flib = nil 227 | end, 228 | ["5.3.3"] = function() 229 | for _, pdata in pairs(global._pdata) do 230 | pdata.flags.autotoggle_unrequested = false 231 | end 232 | end, 233 | ["5.3.9"] = function() 234 | for _, pdata in pairs(global._pdata) do 235 | pdata.gui.sliders = {} 236 | pdata.gui.options = {} 237 | pdata.gui.presets = {} 238 | pdata.gui.networks = {} 239 | end 240 | end, 241 | } 242 | 243 | return migrations -------------------------------------------------------------------------------- /scripts/player-data.lua: -------------------------------------------------------------------------------- 1 | local at_util = require("scripts.util") 2 | local gui_util = require("scripts.gui-util") 3 | local gui = require("__flib__.gui-beta") 4 | local table = require("__flib__.table") 5 | 6 | local player_data = {} 7 | 8 | function player_data.init(player_index) 9 | local player = game.get_player(player_index) 10 | global._pdata[player_index] = { 11 | flags = { 12 | can_open_gui = player.force.character_logistic_requests, 13 | gui_open = false, 14 | dirty = false, 15 | pinned = true, 16 | status_display_open = false, 17 | trash_above_requested = false, 18 | trash_unrequested = false, 19 | trash_network = false, 20 | pause_trash = false, 21 | pause_requests = false, 22 | autotoggle_unrequested = false, 23 | has_temporary_requests = false, 24 | }, 25 | gui = { 26 | import = {}, 27 | main = {}, 28 | sliders = {}, 29 | options = {}, 30 | presets = {}, 31 | networks = {}, 32 | spider = {}, 33 | }, 34 | config_new = {config = {}, by_name = {}, c_requests = 0, max_slot = 0}, 35 | config_tmp = {config = {}, by_name = {}, c_requests = 0, max_slot = 0}, 36 | selected = false, 37 | 38 | current_network = nil, 39 | presets = {}, 40 | temporary_requests = {}, 41 | settings = {}, 42 | selected_presets = {}, 43 | death_presets = {}, 44 | networks = {} 45 | } 46 | player.set_shortcut_available("autotrash-toggle-gui", player.force.character_logistic_requests) 47 | player_data.update_settings(game.get_player(player_index), global._pdata[player_index]) 48 | 49 | global._pdata[player_index].config_tmp = player_data.combine_from_vanilla(player) 50 | global._pdata[player_index].config_new = at_util.copy_preset(global._pdata[player_index].config_tmp) 51 | 52 | return global._pdata[player_index] 53 | end 54 | 55 | function player_data.update_settings(player, pdata) 56 | local player_settings = player.mod_settings 57 | local settings = { 58 | status_count = player_settings["autotrash_status_count"].value, 59 | status_columns = player_settings["autotrash_status_columns"].value, 60 | display_messages = player_settings["autotrash_display_messages"].value, 61 | close_on_apply = player_settings["autotrash_close_on_apply"].value, 62 | reset_on_close = player_settings["autotrash_reset_on_close"].value, 63 | overwrite = player_settings["autotrash_overwrite"].value, 64 | trash_equals_requests = player_settings["autotrash_trash_equals_requests"].value, 65 | columns = player_settings["autotrash_gui_displayed_columns"].value, 66 | rows = player_settings["autotrash_gui_rows_before_scroll"].value, 67 | show_button = player_settings["autotrash_show_button"].value, 68 | } 69 | pdata.settings = settings 70 | end 71 | 72 | function player_data.refresh(player, pdata) 73 | pdata.flags.can_open_gui = player.force.character_logistic_requests 74 | player.set_shortcut_available("autotrash-toggle-gui", player.force.character_logistic_requests) 75 | player_data.update_settings(player, pdata) 76 | end 77 | 78 | function player_data.swap_configs(pdata, origin, destination) 79 | local config_tmp = pdata.config_tmp 80 | local old_config = config_tmp.config[origin] 81 | local tmp = table.deep_copy(config_tmp.config[destination]) 82 | config_tmp.config[destination] = table.deep_copy(old_config) 83 | config_tmp.config[destination].slot = destination 84 | config_tmp.by_name[old_config.name] = config_tmp.config[destination] 85 | if tmp then 86 | config_tmp.config[origin] = tmp 87 | config_tmp.by_name[tmp.name] = tmp 88 | tmp.slot = origin 89 | else 90 | config_tmp.config[origin] = nil 91 | end 92 | config_tmp.max_slot = destination > config_tmp.max_slot and destination or config_tmp.max_slot 93 | end 94 | 95 | function player_data.add_config(pdata, name, min, max, index) 96 | local config_tmp = pdata.config_tmp 97 | config_tmp.config[index] = { 98 | name = name, min = min, 99 | max = max, slot = index 100 | } 101 | config_tmp.by_name[name] = config_tmp.config[index] 102 | 103 | config_tmp.max_slot = index > config_tmp.max_slot and index or config_tmp.max_slot 104 | if config_tmp.config[index].min > 0 then 105 | config_tmp.c_requests = config_tmp.c_requests + 1 106 | end 107 | end 108 | 109 | function player_data.clear_config(pdata, index) 110 | local config_tmp = pdata.config_tmp 111 | local config = config_tmp.config[index] 112 | if config then 113 | if config.min > 0 then 114 | config_tmp.c_requests = config_tmp.c_requests > 0 and config_tmp.c_requests - 1 or 0 115 | end 116 | if pdata.selected == index then pdata.selected = false end 117 | config_tmp.by_name[config.name] = nil 118 | config_tmp.config[index] = nil 119 | if index == config_tmp.max_slot then 120 | config_tmp.max_slot = 0 121 | for i = index-1, 1, -1 do 122 | if config_tmp.config[i] then 123 | config_tmp.max_slot = i 124 | break 125 | end 126 | end 127 | end 128 | end 129 | end 130 | 131 | function player_data.check_config(player, pdata) 132 | local adjusted 133 | for _, config in pairs(pdata.config_tmp.config) do 134 | if config.max < config.min then 135 | adjusted = true 136 | config.max = config.min 137 | player.print{"at-message.adjusted-trash-amount", at_util.item_prototype(config.name).localised_name, config.max} 138 | end 139 | end 140 | return adjusted 141 | end 142 | 143 | function player_data.combine_from_vanilla(player, pdata, name) 144 | if not player.character then 145 | return {config = {}, by_name = {}, c_requests = 0, max_slot = 0} 146 | end 147 | local result = at_util.get_requests(player.get_personal_logistic_slot, player.character.request_slot_count) 148 | if name and next(result.config) then 149 | pdata.presets[name] = at_util.copy_preset(result) 150 | end 151 | return result 152 | end 153 | 154 | function player_data.import_when_empty(player, pdata) 155 | if not next(pdata.config_new.config) then 156 | player.print{"at-message.empty-config"} 157 | player.print{"at-message.auto-import"} 158 | pdata.config_tmp = player_data.combine_from_vanilla(player, "at_imported") 159 | pdata.config_new = at_util.copy_preset(pdata.config_tmp) 160 | pdata.selected = false 161 | return true 162 | end 163 | end 164 | 165 | --mostly taken from https://github.com/raiguard/Factorio-QuickItemSearch/blob/master/src/scripts/player-data.lua 166 | function player_data.find_request(player, item) 167 | local character = player.character 168 | local get_slot = character.get_personal_logistic_slot 169 | local result 170 | local max = character.request_slot_count 171 | for i=1, max do 172 | local slot = get_slot(i) 173 | if tostring(slot.name) == item then 174 | slot.index = i 175 | result = slot 176 | break 177 | end 178 | end 179 | --extend slots if no empty one was found 180 | if not result and item == "nil" then 181 | max = max + 1 182 | result = get_slot(max) 183 | result.index = max 184 | end 185 | return result 186 | end 187 | 188 | function player_data.set_request(player, pdata, request, temporary) 189 | local existing_request 190 | local character = player.character 191 | if request.index then 192 | existing_request = character.get_personal_logistic_slot(request.index) 193 | if tostring(existing_request.name) ~= request.name then 194 | existing_request = player_data.find_request(player, request.name) 195 | if existing_request then 196 | request.index = existing_request.index 197 | else 198 | existing_request = player_data.find_request(player, "nil") 199 | if existing_request then 200 | request.index = existing_request.index 201 | else 202 | player.print("No empty slot found") 203 | return false 204 | end 205 | end 206 | else 207 | existing_request.index = request.index 208 | end 209 | else 210 | existing_request = player_data.find_request(player, "nil") 211 | if existing_request then 212 | request.index = existing_request.index 213 | else 214 | player.print("No empty slot found") 215 | return false 216 | end 217 | end 218 | character.set_personal_logistic_slot(request.index, request) 219 | if temporary then 220 | pdata.temporary_requests[request.name] = {temporary = request, previous = existing_request} 221 | pdata.flags.has_temporary_requests = true 222 | end 223 | return true 224 | end 225 | 226 | function player_data.check_temporary_requests(player, pdata) 227 | local contents = player.get_main_inventory().get_contents() 228 | local cursor_stack = player.cursor_stack 229 | if cursor_stack and cursor_stack.valid_for_read then 230 | contents[cursor_stack.name] = cursor_stack.count + (contents[cursor_stack.name] or 0) 231 | end 232 | 233 | local temporary_requests = pdata.temporary_requests 234 | local character = player.character 235 | local set_request = character.set_personal_logistic_slot 236 | local get_request = character.get_personal_logistic_slot 237 | local clear_request = character.clear_personal_logistic_slot 238 | for name, next_request in pairs(temporary_requests) do 239 | local temporary_request = next_request.temporary 240 | local item_count = contents[name] or 0 241 | 242 | local remove_request = false 243 | local current_request = get_request(temporary_request.index) 244 | if tostring(current_request.name) == temporary_request.name then 245 | if current_request.min ~= temporary_request.min or current_request.max ~= temporary_request.max then 246 | remove_request = true 247 | else 248 | if item_count >= temporary_request.min and item_count <= temporary_request.max then 249 | clear_request(temporary_request.index) 250 | set_request(temporary_request.index, next_request.previous) 251 | remove_request = true 252 | player.print({"at-message.removed-from-temporary-requests", at_util.item_prototype(name).localised_name}) 253 | end 254 | end 255 | else 256 | remove_request = true 257 | end 258 | if remove_request then 259 | temporary_requests[name] = nil 260 | end 261 | end 262 | if not next(temporary_requests) then 263 | pdata.flags.has_temporary_requests = false 264 | end 265 | end 266 | 267 | function player_data.add_preset(player, pdata, name, config) 268 | config = config or pdata.config_tmp 269 | if name == "" then 270 | player.print({"at-message.name-not-set"}) 271 | return 272 | end 273 | if pdata.presets[name] then 274 | if not pdata.settings.overwrite then 275 | player.print({"at-message.name-in-use"}) 276 | return 277 | end 278 | pdata.presets[name] = at_util.copy_preset(config) 279 | player.print({"at-message.preset-updated", name}) 280 | else 281 | pdata.presets[name] = at_util.copy_preset(config) 282 | if (player.controller_type ~= defines.controllers.editor) then 283 | gui.build(pdata.gui.presets.scroll, {gui_util.preset(name, pdata)}) 284 | end 285 | end 286 | return true 287 | 288 | end 289 | 290 | return player_data -------------------------------------------------------------------------------- /scripts/presets.lua: -------------------------------------------------------------------------------- 1 | local at_util = require("scripts.util") 2 | local max_request = require("constants").max_request 3 | local presets = {} 4 | 5 | --merge 2 presets, 6 | --requests and trash are set to max(current, preset) 7 | --if one preset has trash set to false it is set to a non false value 8 | --slot from current are kept 9 | function presets.merge(current, preset, append) 10 | if not (preset and preset.config) then return end 11 | local result = current.config 12 | local b = at_util.copy_preset(preset) 13 | local no_slot = {} 14 | local max_slot = current.max_slot 15 | local c_requests = current.c_requests 16 | 17 | for _, result_config in pairs(result) do 18 | if result_config.name then 19 | local tmp = result_config 20 | local config = b.by_name[result_config.name] 21 | if config then 22 | tmp.min = (config.min > tmp.min) and config.min or tmp.min 23 | tmp.max = (config.max < max_request and tmp.max < max_request and config.max > tmp.max) and config.max or tmp.max 24 | tmp.max = tmp.max < tmp.min and tmp.min or tmp.max 25 | b.config[config.slot] = nil 26 | max_slot = max_slot > tmp.slot and max_slot or tmp.slot 27 | c_requests = tmp.min > 0 and c_requests + 1 or c_requests 28 | current.by_name[tmp.name] = tmp 29 | end 30 | end 31 | end 32 | if append then 33 | local i = max_slot + 1 34 | local last 35 | for slot, config in pairs(b.config) do 36 | if last then 37 | i = i + slot - last 38 | end 39 | config.slot = i 40 | result[i] = config 41 | current.by_name[config.name] = config 42 | last = slot 43 | max_slot = max_slot > i and max_slot or i 44 | c_requests = config.min > 0 and c_requests + 1 or c_requests 45 | end 46 | else 47 | --preserve slot number if possible 48 | for _, config in pairs(b.config) do 49 | if not result[config.slot] then 50 | result[config.slot] = config 51 | current.by_name[config.name] = config 52 | else 53 | no_slot[#no_slot + 1] = config 54 | end 55 | max_slot = max_slot > config.slot and max_slot or config.slot 56 | c_requests = config.min > 0 and c_requests + 1 or c_requests 57 | end 58 | end 59 | 60 | local start = 1 61 | for _, s in pairs(no_slot) do 62 | for i = start, max_slot + #no_slot do 63 | if not result[i] then 64 | s.slot = i 65 | result[i] = s 66 | current.by_name[s.name] = s 67 | start = i + 1 68 | max_slot = max_slot > i and max_slot or i 69 | break 70 | end 71 | end 72 | end 73 | current.max_slot = max_slot 74 | current.c_requests = c_requests 75 | return current, max_slot, c_requests 76 | end 77 | 78 | local ceil = math.ceil 79 | --creates a blueprint with 2 rows of constant combinators 80 | --first row for requests, second for trash (signal omitted when no trash value is set) 81 | --preserves slot order, empty combinators are not included 82 | -- for importing, slot can be recalculated by the x position: starting_slot = x * 18 + 1 83 | -- y position of 0: request, y = 4: trash 84 | function presets.export(preset, name) 85 | local item_slot_count = game.entity_prototypes["constant-combinator"].item_slot_count 86 | local combinators = ceil(preset.max_slot / item_slot_count) 87 | local half_cc = ceil(combinators / 2) 88 | local start 89 | local request_cc = {} 90 | local trash_cc = {} 91 | local bp = {} 92 | local item_config, request_items, trash_items, item_signal, pos_x 93 | local index_offset, index 94 | combinators = combinators > 0 and combinators or 1 95 | for cc = 1, combinators do 96 | pos_x = cc - 1 97 | index_offset = pos_x * item_slot_count 98 | start = index_offset + 1 99 | request_cc[cc] = {entity_number = cc, name = "constant-combinator", position = {x = pos_x, y = 0}, control_behavior = {filters = {}}} 100 | trash_cc[cc] = {entity_number = cc + half_cc, name = "constant-combinator", position = {x = pos_x, y = 4}, control_behavior = {filters = {}}} 101 | request_items = request_cc[cc].control_behavior.filters 102 | trash_items = trash_cc[cc].control_behavior.filters 103 | for i = start, start + item_slot_count - 1 do 104 | item_config = preset.config[i] 105 | if item_config then 106 | index = item_config.slot - index_offset 107 | item_signal = {name = item_config.name, type = "item"} 108 | request_items[#request_items+1] = {index = index, count = item_config.min, signal = item_signal} 109 | if item_config.max < max_request then 110 | trash_items[#trash_items+1] = {index = index, count = item_config.max, signal = item_signal} 111 | end 112 | end 113 | end 114 | --maybe skip empty combinators (can mess with entity_number but does it matter?) 115 | bp[#bp+1] = request_cc[cc] 116 | bp[#bp+1] = trash_cc[cc] 117 | end 118 | local icons = {{index = 1, signal = {name = "signal-A", type = "virtual"}},{index = 2, signal = {name = "signal-T", type = "virtual"}},{index = 3, signal = {name = "signal-0", type = "virtual"}}} 119 | local inventory = game.create_inventory(1) 120 | inventory.insert{name = "blueprint"} 121 | local stack = inventory[1] 122 | stack.set_blueprint_entities(bp) 123 | stack.label = name 124 | stack.blueprint_icons = icons 125 | local result = stack.export_stack() 126 | inventory.destroy() 127 | return result 128 | end 129 | 130 | function presets.export_all(pdata) 131 | local inventory = game.create_inventory(1) 132 | inventory.insert{name = "blueprint-book"} 133 | local book = inventory[1] 134 | local book_inventory = book.get_inventory(defines.inventory.item_main) 135 | local index = 1 136 | for name, preset in pairs(pdata.presets) do 137 | book_inventory.insert{name = "blueprint"} 138 | local bp = presets.export(preset, name) 139 | local blueprint = book_inventory[index] 140 | blueprint.import_stack(bp) 141 | index = index + 1 142 | end 143 | local result = book.export_stack() 144 | inventory.destroy() 145 | return result 146 | end 147 | 148 | --Storing the exported string in the blueprint library preserves it even when mod items have been removed 149 | --Importing a string with invalid item signals removes the combinator containing the invalid signals. 150 | function presets.import(preset, icons) 151 | local item_slot_count = game.entity_prototypes["constant-combinator"].item_slot_count 152 | local tmp = {config = {}, by_name = {}, max_slot = 0, c_requests = 0} 153 | local by_name = tmp.by_name 154 | local config = tmp.config 155 | local index_offset, index 156 | local cc_found = false 157 | local missing_items = {} 158 | if icons then 159 | --log_blueprint_entities(preset) 160 | for _, cc in pairs(preset) do 161 | index_offset = (cc.position.x - 0.5) * item_slot_count 162 | local filters = cc.control_behavior and cc.control_behavior.filters or {} 163 | if cc.name == "constant-combinator" then 164 | cc_found = true 165 | for _, item_config in pairs(filters) do 166 | if at_util.item_prototype(item_config.signal.name) then 167 | index = index_offset + item_config.index 168 | if not config[index] then 169 | config[index] = {name = item_config.signal.name, slot = index, max = max_request, min = 0} 170 | by_name[item_config.signal.name] = config[index] 171 | end 172 | if (cc.position.y - 0.5) == 0 then 173 | config[index].min = item_config.count 174 | else 175 | config[index].max = item_config.count 176 | end 177 | tmp.max_slot = tmp.max_slot > index and tmp.max_slot or index 178 | tmp.c_requests = config[index].min > 0 and (tmp.c_requests + 1) or tmp.c_requests 179 | else 180 | table.insert(missing_items, item_config.signal.name) 181 | end 182 | end 183 | end 184 | end 185 | end 186 | return tmp, cc_found, missing_items 187 | end 188 | 189 | return presets -------------------------------------------------------------------------------- /scripts/spidertron.lua: -------------------------------------------------------------------------------- 1 | local gui = require("__flib__.gui-beta") 2 | local presets = require("scripts.presets") 3 | local at_util = require("scripts.util") 4 | local gui_util = require("scripts.gui-util") 5 | local player_data = require("scripts.player-data") 6 | local constants = require("constants") 7 | local spider_gui = {} 8 | 9 | local function set_requests(spider, requests, keep_presets) 10 | local set_request = spider.set_vehicle_logistic_slot 11 | local clear = spider.clear_vehicle_logistic_slot 12 | local config = requests.config 13 | local request_slot_count = spider.request_slot_count 14 | if keep_presets then 15 | local result = at_util.get_requests(spider.get_vehicle_logistic_slot, request_slot_count) 16 | local tmp = presets.merge(result, requests) 17 | for _, data in pairs(tmp.by_name) do 18 | set_request(data.slot, data) 19 | end 20 | else 21 | for i = 1, requests.max_slot do 22 | if config[i] then 23 | set_request(i, config[i]) 24 | else 25 | clear(i) 26 | end 27 | end 28 | if request_slot_count > requests.max_slot then 29 | for i = requests.max_slot + 1, request_slot_count do 30 | clear(i) 31 | end 32 | end 33 | end 34 | end 35 | 36 | local collapse_sprites = { 37 | [true] = { 38 | sprite="utility/collapse", 39 | hovered_sprite="utility/collapse_dark", 40 | clicked_sprite="utility/collapse_dark" 41 | }, 42 | [false] = { 43 | sprite="utility/expand", 44 | hovered_sprite="utility/expand_dark", 45 | clicked_sprite="utility/expand_dark" 46 | } 47 | } 48 | 49 | spider_gui.handlers = { 50 | load = function(e, msg) 51 | set_requests(e.entity, e.pdata.presets[msg.name], e.shift) 52 | end, 53 | collapse = function(e) 54 | local frame = e.pdata.gui.spider.preset_frame 55 | local visible = not frame.visible 56 | frame.visible = visible 57 | for k, v in pairs(collapse_sprites[visible]) do 58 | e.element[k] = v 59 | end 60 | frame.parent.style.bottom_padding = visible and 8 or 0 61 | end, 62 | save = function(e) 63 | local textfield = e.pdata.gui.spider.preset_textfield 64 | local config = at_util.get_requests(e.entity.get_vehicle_logistic_slot, e.entity.request_slot_count) 65 | if player_data.add_preset(e.player, e.pdata, textfield.text, config) then 66 | spider_gui.update(e.player, e.pdata) 67 | textfield.text = "" 68 | end 69 | end, 70 | textfield = function(e) 71 | e.element.select_all() 72 | end, 73 | trash_all = function(e) 74 | set_requests(e.entity, global.trash_all_items, true) 75 | end, 76 | } 77 | 78 | function spider_gui.presets(pdata) 79 | local ret = {} 80 | local i = 1 81 | for name in pairs(pdata.presets) do 82 | ret[i] = {type = "flow", direction = "horizontal", name = name, children = { 83 | { 84 | type = "button", style = "at_preset_button", caption = name, 85 | actions = {on_click = {gui = "spider", action = "load", name = name}} 86 | }, 87 | {type = "sprite-button", style = "at_delete_preset", sprite = "utility/trash", 88 | actions = {on_click = {gui = "presets", action = "delete", spider = true}}, 89 | } 90 | }} 91 | i = i + 1 92 | end 93 | ret[#ret+1] = { 94 | type = "button", style = "red_button", style_mods = {width = 182}, 95 | caption = {"at-gui.spider-trash-all"}, 96 | tooltip = {"at-gui.spider-trash-all-tt"}, 97 | actions = {on_click = {gui = "spider", action = "trash_all"}} 98 | } 99 | return ret 100 | end 101 | 102 | function spider_gui.init(player, pdata) 103 | spider_gui.destroy(pdata) 104 | local refs = gui.build(player.gui.relative, { 105 | {type = "frame", style = "inner_frame_in_outer_frame", direction = "vertical", 106 | style_mods = {maximal_height = constants.gui_dimensions.spidertron}, 107 | ref = {"main"}, 108 | anchor = {gui = defines.relative_gui_type.spider_vehicle_gui, position = defines.relative_gui_position.right},--luacheck: ignore 109 | children = { 110 | {type = "flow", children = { 111 | {type = "label", style = "frame_title", caption = "Logistics", elem_mods = {ignored_by_interaction = true}}, 112 | {type = "empty-widget", style = "flib_titlebar_drag_handle", elem_mods = {ignored_by_interaction = true}}, 113 | gui_util.frame_action_button("utility/collapse", "utility/collapse_dark", 114 | {gui = "spider", action = "collapse"} 115 | ), 116 | }}, 117 | {type = "frame", style = "inside_shallow_frame", direction = "vertical", ref = {"preset_frame"}, children = { 118 | {type = "frame", style = "subheader_frame", style_mods = {left_padding = 8}, children={ 119 | {type = "textfield", style = "long_number_textfield", ref = {"preset_textfield"}, 120 | actions = {on_click = {gui = "spider", action = "textfield"}}, 121 | }, 122 | gui_util.pushers.horizontal, 123 | {type = "sprite-button", sprite = "utility/check_mark",style = "item_and_count_select_confirm", 124 | tooltip = {"at-gui.spider-save"}, 125 | actions = {on_click = {gui = "spider", action = "save"}} 126 | } 127 | }}, 128 | {type = "flow", direction="vertical", 129 | style_mods = {padding = 12, top_padding = 8, vertical_spacing = 12}, 130 | children = { 131 | {type = "frame", style = "deep_frame_in_shallow_frame", children = { 132 | {type = "scroll-pane", style = "at_right_scroll_pane", ref = {"presets"}, 133 | style_mods = {vertically_stretchable = false}, 134 | children = spider_gui.presets(pdata) 135 | } 136 | }}, 137 | }} 138 | }}, 139 | } 140 | } 141 | }) 142 | pdata.gui.spider = refs 143 | end 144 | 145 | function spider_gui.update(player, pdata, hide) 146 | if not (player.opened_gui_type == defines.gui_type.entity and player.opened and player.opened.type == "spider-vehicle") then 147 | return 148 | end 149 | local gui_spider = pdata.gui.spider and pdata.gui.spider.presets 150 | if not (gui_spider and gui_spider.valid) then 151 | spider_gui.init(player, pdata) 152 | end 153 | pdata.gui.spider.presets.clear() 154 | gui.build(pdata.gui.spider.presets, spider_gui.presets(pdata)) 155 | pdata.gui.spider.main.visible = not hide 156 | end 157 | 158 | function spider_gui.destroy(pdata) 159 | if not (pdata.gui.spider and pdata.gui.spider.main and pdata.gui.spider.main.valid) then 160 | return 161 | end 162 | pdata.gui.spider.main.destroy() 163 | pdata.gui.spider = nil 164 | end 165 | 166 | return spider_gui -------------------------------------------------------------------------------- /scripts/util.lua: -------------------------------------------------------------------------------- 1 | local constants = require("constants") 2 | 3 | local M = {} 4 | 5 | local item_prototypes = {} 6 | function M.item_prototype(name) 7 | if item_prototypes[name] then 8 | return item_prototypes[name] 9 | end 10 | item_prototypes[name] = game.item_prototypes[name] 11 | return item_prototypes[name] 12 | end 13 | 14 | function M.copy_preset(preset) 15 | local new_table = {config = {}, by_name = {}, c_requests = preset.c_requests, max_slot = preset.max_slot} 16 | local nt_config = new_table.config 17 | local nt_by_name = new_table.by_name 18 | for i, config in pairs(preset.config) do 19 | nt_config[i] = {name = config.name, min = config.min, max = config.max, slot = config.slot} 20 | nt_by_name[config.name] = nt_config[i] 21 | end 22 | return new_table 23 | end 24 | 25 | -- function M.get_quickbar_items(player, pdata) 26 | -- local items = {} 27 | -- local quickbars = 4 28 | -- local get_active_page = player.get_active_quick_bar_page 29 | -- local get_slot = player.get_quick_bar_slot 30 | -- for screen_index = 1, quickbars do 31 | -- local offset = (get_active_page(screen_index) - 1) * 10 32 | -- local start = offset + 1 33 | -- for i = start, start + 9 do 34 | -- local item = get_slot(i) 35 | -- if item then 36 | -- local slot = (i - offset)+((screen_index-1)*10) 37 | -- assert(not items[slot]) 38 | -- items[slot] = item.name 39 | -- end 40 | -- end 41 | -- end 42 | -- return items 43 | -- end 44 | 45 | -- function M.set_requests2(player, pdata) 46 | -- local character = player.character 47 | -- if not character then return end 48 | -- local flags = pdata.flags 49 | -- local config_new = pdata.config_new 50 | -- local storage = config_new.config 51 | -- local slot_count = player.character_logistic_slot_count 52 | -- local set_request_slot = character.set_personal_logistic_slot 53 | -- local clear_request_slot = character.clear_personal_logistic_slot 54 | -- local trash_paused = flags.pause_trash 55 | -- local trash_above_requested = flags.trash_above_requested 56 | -- local requests_paused = flags.pause_requests 57 | -- local contents = flags.trash_unrequested and player.get_main_inventory().get_contents() 58 | -- local temporary_requests = pdata.temporary_requests 59 | -- local handled_temporary = {} 60 | 61 | -- local quickbars = 4 62 | -- local needed_slots = quickbars*10 + config_new.max_slot 63 | -- if needed_slots > slot_count then 64 | -- player.character_logistic_slot_count = needed_slots 65 | -- slot_count = needed_slots 66 | -- end 67 | -- local max_request = constants.max_request 68 | -- local quickbar_items = M.get_quickbar_items(player, pdata) 69 | -- local already_requested = {} 70 | -- for i = 1, quickbars * 10 do 71 | -- clear_request_slot(i) 72 | -- local name = quickbar_items[i] 73 | -- if name and not constants.trash_blacklist[M.item_prototype(name).type] then 74 | -- local min = config_new.by_name[name] and config_new.by_name[name].min or M.item_prototype(name).stack_size 75 | -- local max = config_new.by_name[name] and config_new.by_name[name].max or max_request 76 | -- set_request_slot(i, {name = name, min = min, max = max}) 77 | -- already_requested[name] = true 78 | -- end 79 | -- end 80 | -- local offset = quickbars * 10 81 | -- for c = offset + 1, slot_count do 82 | -- local min 83 | -- local max = max_request 84 | -- local req = storage[c - offset] 85 | -- if req and not already_requested[req.name] then 86 | -- local name = req.name 87 | -- if temporary_requests[name] then 88 | -- req = temporary_requests[name].temporary 89 | -- handled_temporary[name] = true 90 | -- end 91 | -- local request = req.min 92 | -- min = requests_paused and 0 or request 93 | -- if not trash_paused then 94 | -- max = (trash_above_requested and request > 0) and request or req.max 95 | -- if contents and contents[name] then 96 | -- contents[name] = nil 97 | -- end 98 | -- end 99 | -- set_request_slot(c, {name = name, min = min, max = max}) 100 | -- else 101 | -- clear_request_slot(c) 102 | -- end 103 | -- end 104 | 105 | -- --handle remaining temporary requests 106 | -- for name, request_data in pairs(temporary_requests) do 107 | -- if not handled_temporary[name] then 108 | -- local temp_request = request_data.temporary 109 | -- set_request_slot(temp_request.index, temp_request) 110 | -- if contents and contents[name] then 111 | -- contents[name] = nil 112 | -- end 113 | -- end 114 | -- end 115 | 116 | -- --trash unrequested items 117 | -- if contents and not trash_paused then 118 | -- if not next(contents) then return end 119 | 120 | -- for name, _ in pairs(contents) do 121 | -- if constants.trash_blacklist[M.item_prototype(name).type] then 122 | -- contents[name] = nil 123 | -- end 124 | -- end 125 | -- local c_contents = table_size(contents) 126 | -- if slot_count < config_new.max_slot + c_contents then 127 | -- player.character_logistic_slot_count = slot_count + c_contents 128 | -- end 129 | 130 | -- local i = config_new.max_slot + 1 131 | -- for name, _ in pairs(contents) do 132 | -- set_request_slot(i, {name = name, max = 0}) 133 | -- i = i + 1 134 | -- end 135 | -- end 136 | -- end 137 | 138 | function M.set_requests(player, pdata) 139 | local character = player.character 140 | if not character then return end 141 | local flags = pdata.flags 142 | local config_new = pdata.config_new 143 | local storage = config_new.config 144 | local slot_count = player.character.request_slot_count 145 | local set_request_slot = character.set_personal_logistic_slot 146 | local clear_request_slot = character.clear_personal_logistic_slot 147 | local trash_paused = flags.pause_trash 148 | local trash_above_requested = flags.trash_above_requested 149 | local requests_paused = flags.pause_requests 150 | local contents = flags.trash_unrequested and player.get_main_inventory().get_contents() 151 | local temporary_requests = pdata.temporary_requests 152 | local handled_temporary = {} 153 | 154 | if config_new.max_slot > slot_count then 155 | slot_count = config_new.max_slot 156 | end 157 | 158 | local max_request = constants.max_request 159 | for c = 1, slot_count do 160 | local min 161 | local max = max_request 162 | local req = storage[c] 163 | if req then 164 | local name = req.name 165 | if temporary_requests[name] then 166 | req = temporary_requests[name].temporary 167 | handled_temporary[name] = true 168 | end 169 | local request = req.min 170 | min = requests_paused and 0 or request 171 | if not trash_paused then 172 | max = (trash_above_requested and request > 0) and request or req.max 173 | if contents and contents[name] then 174 | contents[name] = nil 175 | end 176 | end 177 | set_request_slot(c, {name = name, min = min, max = max}) 178 | else 179 | clear_request_slot(c) 180 | end 181 | end 182 | 183 | --handle remaining temporary requests 184 | for name, request_data in pairs(temporary_requests) do 185 | if not handled_temporary[name] then 186 | local temp_request = request_data.temporary 187 | set_request_slot(temp_request.index, temp_request) 188 | if contents and contents[name] then 189 | contents[name] = nil 190 | end 191 | end 192 | end 193 | 194 | --trash unrequested items 195 | if contents and not trash_paused then 196 | if not next(contents) then 197 | if pdata.flags.autotoggle_unrequested then 198 | pdata.flags.trash_unrequested = false 199 | end 200 | return true 201 | end 202 | 203 | for name, _ in pairs(contents) do 204 | if constants.trash_blacklist[M.item_prototype(name).type] then 205 | contents[name] = nil 206 | end 207 | end 208 | local i = config_new.max_slot + 1 209 | for name, _ in pairs(contents) do 210 | set_request_slot(i, {name = name, max = 0}) 211 | i = i + 1 212 | end 213 | end 214 | end 215 | 216 | function M.pause_requests(player, pdata) 217 | pdata.flags.pause_requests = true 218 | M.set_requests(player, pdata) 219 | end 220 | 221 | function M.unpause_requests(player, pdata) 222 | pdata.flags.pause_requests = false 223 | M.set_requests(player, pdata) 224 | end 225 | 226 | function M.pause_trash(player, pdata) 227 | pdata.flags.pause_trash = true 228 | M.set_requests(player, pdata) 229 | end 230 | 231 | function M.unpause_trash(player, pdata) 232 | pdata.flags.pause_trash = false 233 | M.set_requests(player, pdata) 234 | end 235 | 236 | function M.get_non_equipment_network(character) 237 | if not character then return end 238 | --trash slots researched 239 | local logi_point = character.get_logistic_point(defines.logistic_member_index.character_provider) 240 | if not logi_point then 241 | --requests researched 242 | logi_point = character.get_logistic_point(defines.logistic_member_index.character_requester) 243 | end 244 | return logi_point and logi_point.logistic_network 245 | end 246 | 247 | function M.get_requests(get_request_slot, request_slot_count) 248 | local by_name = {} 249 | local requests = {} 250 | local count = 0 251 | for c = 1, request_slot_count do 252 | local t = get_request_slot(c) 253 | if t.name then 254 | requests[c] = {name = t.name, min = t.min, max = t.max, slot = c} 255 | by_name[t.name] = requests[c] 256 | count = t.min > 0 and count + 1 or count 257 | end 258 | end 259 | return {config = requests, by_name = by_name, max_slot = request_slot_count, c_requests = count} 260 | end 261 | function M.get_network_entity(player) 262 | local network = M.get_non_equipment_network(player.character) 263 | if network and network.valid then 264 | local cell = network.find_cell_closest_to(player.position) 265 | return cell and cell.owner 266 | end 267 | return false 268 | end 269 | 270 | function M.in_network(player, pdata) 271 | if not pdata.flags.trash_network then 272 | return true 273 | end 274 | local currentNetwork = M.get_non_equipment_network(player.character) 275 | if not (currentNetwork and currentNetwork.valid) then 276 | return false 277 | end 278 | for id, network in pairs(pdata.networks) do 279 | if network and network.valid then 280 | if currentNetwork == network.logistic_network then 281 | return true 282 | end 283 | elseif network and not network.valid then 284 | player.print({"at-message.network-lost", id}) 285 | pdata.networks[id] = nil 286 | end 287 | end 288 | return false 289 | end 290 | 291 | function M.format_number(n, append_suffix) 292 | local amount = tonumber(n) 293 | if not amount then 294 | return n 295 | end 296 | local suffix = "" 297 | if append_suffix then 298 | local suffix_list = { 299 | ["T"] = 1000000000000, 300 | ["B"] = 1000000000, 301 | ["M"] = 1000000, 302 | ["k"] = 1000 303 | } 304 | local floor = math.floor 305 | local abs = math.abs 306 | for letter, limit in pairs (suffix_list) do 307 | if abs(amount) >= limit then 308 | amount = floor(amount/(limit/10))/10 309 | suffix = letter 310 | break 311 | end 312 | end 313 | end 314 | local formatted = amount 315 | local k 316 | local gsub = string.gsub 317 | while true do 318 | formatted, k = gsub(formatted, "^(-?%d+)(%d%d%d)", '%1,%2') 319 | if (k == 0) then 320 | break 321 | end 322 | end 323 | return formatted..suffix 324 | end 325 | 326 | function M.remove_invalid_items() 327 | local function _remove(tbl) 328 | local max_slot = #tbl.config 329 | if not type(tbl.max_slot) == "number" then 330 | for j = 1, #tbl.config do 331 | if tbl.config[j] and tbl.config[j].slot > max_slot then 332 | max_slot = tbl.config[j].slot 333 | end 334 | end 335 | end 336 | tbl.max_slot = max_slot 337 | for i = tbl.max_slot, 1, -1 do 338 | local item_config = tbl.config[i] 339 | if item_config then 340 | if not M.item_prototype(item_config.name) then 341 | if tbl.config[i].min > 0 then 342 | tbl.c_requests = tbl.c_requests - 1 343 | end 344 | tbl.by_name[item_config.name] = nil 345 | tbl.config[i] = nil 346 | if tbl.max_slot == i then 347 | tbl.max_slot = false 348 | end 349 | else 350 | tbl.max_slot = tbl.max_slot or i 351 | end 352 | end 353 | end 354 | end 355 | for _, pdata in pairs(global._pdata) do 356 | if pdata.config_new and pdata.config_tmp then 357 | _remove(pdata.config_new) 358 | _remove(pdata.config_tmp) 359 | end 360 | if pdata.presets then 361 | for _, stored in pairs(pdata.presets) do 362 | _remove(stored) 363 | end 364 | end 365 | end 366 | end 367 | 368 | return M -------------------------------------------------------------------------------- /settings.lua: -------------------------------------------------------------------------------- 1 | data:extend({ 2 | { 3 | type = "int-setting", 4 | name = "autotrash_update_rate", 5 | setting_type = "runtime-global", 6 | default_value = 120, 7 | minimum_value = 1, 8 | order = "a" 9 | }, 10 | }) 11 | 12 | --per user 13 | data:extend({ 14 | { 15 | type = "bool-setting", 16 | name = "autotrash_show_button", 17 | setting_type = "runtime-per-user", 18 | default_value = true, 19 | order = "a" 20 | }, 21 | { 22 | type = "bool-setting", 23 | name = "autotrash_trash_equals_requests", 24 | setting_type = "runtime-per-user", 25 | default_value = false, 26 | order = "b" 27 | }, 28 | { 29 | type = "bool-setting", 30 | name = "autotrash_overwrite", 31 | setting_type = "runtime-per-user", 32 | default_value = true, 33 | order = "c" 34 | }, 35 | { 36 | type = "bool-setting", 37 | name = "autotrash_reset_on_close", 38 | setting_type = "runtime-per-user", 39 | default_value = false, 40 | order = "d" 41 | }, 42 | { 43 | type = "bool-setting", 44 | name = "autotrash_close_on_apply", 45 | setting_type = "runtime-per-user", 46 | default_value = true, 47 | order = "e" 48 | }, 49 | { 50 | type = "int-setting", 51 | name = "autotrash_gui_displayed_columns", 52 | setting_type = "runtime-per-user", 53 | default_value = 10, 54 | minimum_value = 5, 55 | maximum_value = 40, 56 | allowed_values = {5, 10, 15, 20, 25, 30, 35, 40}, 57 | order = "v" 58 | }, 59 | { 60 | type = "int-setting", 61 | name = "autotrash_gui_rows_before_scroll", 62 | setting_type = "runtime-per-user", 63 | default_value = 6, 64 | minimum_value = 1, 65 | order = "w" 66 | }, 67 | { 68 | type = "int-setting", 69 | name = "autotrash_status_count", 70 | setting_type = "runtime-per-user", 71 | default_value = 10, 72 | minimum_value = 1, 73 | order = "x" 74 | }, 75 | { 76 | type = "int-setting", 77 | name = "autotrash_status_columns", 78 | setting_type = "runtime-per-user", 79 | default_value = 1, 80 | minimum_value = 1, 81 | order = "y" 82 | }, 83 | { 84 | type = "bool-setting", 85 | name = "autotrash_display_messages", 86 | setting_type = "runtime-per-user", 87 | default_value = true, 88 | order = "z" 89 | }, 90 | }) 91 | -------------------------------------------------------------------------------- /thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Choumiko/AutoTrash/4c45ef066720670a4ac23f6d4dd0d7a58af3b27c/thumbnail.png --------------------------------------------------------------------------------