diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index dfe0770..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..b36d3cc --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,32 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "factoriomod", + "request": "launch", + "name": "Factorio Mod Debug", + "factorioPath": "C:/Program Files (x86)/Steam/steamapps/common/Factorio/bin/x64/factorio.exe", + "modsPath": "C:/Users/leonv/OneDrive/Dokumente/VSCode" + }, + { + "type": "factoriomod", + "request": "launch", + "name": "Factorio Mod Debug (Settings & Data)", + "factorioPath": "C:/Program Files (x86)/Steam/steamapps/common/Factorio/bin/x64/factorio.exe", + "modsPath": "C:/Users/leonv/OneDrive/Dokumente/VSCode", + "hookSettings": true, + "hookData": true + }, + { + "type": "factoriomod", + "request": "launch", + "name": "Factorio Mod Debug (Profile)", + "factorioPath": "C:/Program Files (x86)/Steam/steamapps/common/Factorio/bin/x64/factorio.exe", + "modsPath": "C:/Users/leonv/OneDrive/Dokumente/VSCode", + "hookMode": "profile" + } + ] +} \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..c2221fe --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 ickputzdirwech + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..654721d --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +## Overview +If any locomotive or wagon is destroyed the train it belongs to is brought to an immediate halt and a ghost is created. This ghost contains by default requests for all fuel and equipment that was inside the destroyed rolling stock. Inventory filters and limits are saved and restored as well. As soon as the train is complete again it will be set to automatic mode, if it was in that mode originally. + + +## Settings +The following map settings are available: + +* Automatically enable automatic mode (trains will be set to automatic mode again as soon as all entity ghosts were built). Default = true +* Create item requests for destroyed equipment. Default = true +* Create item requests for fuel. Default = true +* Name of the fuel that should be requested (if left empty the destroyed fuel will be requested). Default = empty +* How much of the fuel should be requested. Default = 1 + + +## Known issues +* Locomotives sometimes don't have the right orientation. +* The check whether a train is complete again is relatively simple. It doesn't wait for all item requests to be fulfilled and can't tell the difference between different entities of the same type. +* Equipment is inserted into cargo inventory first and only moved into the grid if the item request is fulfilled completely or otherwise destroyed. +* The item request icons in ghosts for wagons are, unlike for locomotives, not grouped. diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..5eea1da --- /dev/null +++ b/TODO.md @@ -0,0 +1,26 @@ +# ALTERNATIVE SOLUTION: +* Placeholder wagons +* Special train station where trains are fixed +* What if all locomotives are destroyed? + - allow train to only go to specific station + - low top speed +* Problems crashing into each other +* Startup setting +* + + +# LIMITAIONS: +* Equipment is inserted into cargo inventory first and only moved if the item request is fulfilled completey. +* The check whether a train is complete again is relatively simple. It doesn't wait for all item requests to be fulfilled and can't tell the difference between different entities of the same type. +* + + +# VANILLA BUGS: +* Orientation of locomotives. --> REPORTED +* + + +# INTERFACE REQUESTS: +* Way to cluster item-request icons in ghosts like it does for locomotives. --> REQUEST POSTED +* Let item-request-proxy target specific inventories/grid. --> REQUEST POSTED +* diff --git a/control.lua b/control.lua new file mode 100644 index 0000000..2194eee --- /dev/null +++ b/control.lua @@ -0,0 +1,168 @@ +-- ON ENTITY DIED +script.on_event(defines.events.on_entity_died, function(event) + local entity = event.entity + local train = entity.train + -- stops the train + train.speed = 0 + -- creates ghost entity + local ghost_entity = game.surfaces[entity.surface.name].create_entity{ + name = "entity-ghost", + inner_name = entity.name, + position = entity.position, + direction = entity.direction, + force = entity.force, + create_build_effect_smoke = false, + } + -- checks if destroyed entity had any special properties + local fuel_inv = entity.get_fuel_inventory() + local wagon_inv = entity.get_inventory(defines.inventory.cargo_wagon) + local request_proxies = game.surfaces[entity.surface.name].find_entities_filtered{type = "item-request-proxy", name = "item-request-proxy", position = entity.position} + local found_proxies = false + for _, proxy in pairs(request_proxies) do + if proxy.proxy_target == entity then + found_proxies = true + break + end + end + if train.manual_mode == false or (entity.grid and entity.grid.equipment[1]) or (fuel_inv and fuel_inv.is_empty() == false) or (wagon_inv.is_filtered() or (wagon_inv.supports_bar() and (wagon_inv.get_bar() <= #wagon_inv))) or found_proxies then + -- registers the ghost entity + local registration_number = script.register_on_entity_destroyed(ghost_entity) + -- saves type, name and position + if global.ick_destroyed_train == nil then + global.ick_destroyed_train = {} + end + global.ick_destroyed_train[registration_number] = {type = entity.type, name = entity.name, position = entity.position} + -- saves number of carriages per type if train is in automatic mode + if train.manual_mode == false then + local carriages = {["locomotive"] = 0, ["cargo-wagon"] = 0, ["fluid-wagon"] = 0, ["artillery-wagon"] = 0} + for _, entity in pairs(train.carriages) do + carriages[entity.type] = carriages[entity.type] + 1 + end + global.ick_destroyed_train[registration_number].train = carriages + end + -- adds request for fuel + local requests = {} + if settings.global["ick-include-fuel"].value then + if game.item_prototypes[settings.global["ick-fuel-type"].value] then + requests = {[settings.global["ick-fuel-type"].value] = settings.global["ick-fuel-amount"].value} + elseif fuel_inv and fuel_inv.is_empty() == false then + requests = fuel_inv.get_contents() + end + end + -- adds request for equipment + if settings.global["ick-include-equipment"].value and entity.grid and entity.grid.equipment[1] then + for name, count in pairs(entity.grid.get_contents()) do + if requests[name] then + requests[name] = requests[name] + count + else + requests[name] = count + end + end + -- save equipment if entity is a cargo wagon + if entity.type == "cargo-wagon" then + global.ick_destroyed_train[registration_number].equipment = entity.grid.get_contents() + end + end + -- adds request for item-request-proxies + if found_proxies then + for _, proxy in pairs(request_proxies) do + if proxy.proxy_target == entity then + for name, count in pairs(proxy.item_requests) do + if requests[name] then + requests[name] = requests[name] + count + else + requests[name] = count + end + end + end + end + end + ghost_entity.item_requests = requests + -- saves inventory filters + if wagon_inv.is_filtered() then + local filters = {} + for i = 1, #wagon_inv do + table.insert(filters, i, wagon_inv.get_filter(i)) + end + global.ick_destroyed_train[registration_number].filters = filters + end + -- saves inventory limit + if wagon_inv.supports_bar() and (wagon_inv.get_bar() <= #wagon_inv) then + global.ick_destroyed_train[registration_number].bar = wagon_inv.get_bar() + end + end +end, {{filter = "rolling-stock"}}) + + +-- ON ENTITY BUILT +local function built_entity(entity) + if global.ick_destroyed_train then + for i, stored_info in pairs(global.ick_destroyed_train) do + if stored_info.type == entity.type and stored_info.name == entity.name and stored_info.position.x == entity.position.x and stored_info.position.y == entity.position.y then + -- Find and register the created item request proxy + local request_proxy = game.surfaces[entity.surface.name].find_entity("item-request-proxy", entity.position) + if stored_info.equipment and request_proxy then + local registration_number = script.register_on_entity_destroyed(request_proxy) + global.ick_destroyed_train[registration_number] = {position = entity.position, requests = stored_info.equipment, target = entity} + end + -- sets inventory filters + if stored_info.filters then + for i = 1, #entity.get_inventory(defines.inventory.cargo_wagon) do + entity.get_inventory(defines.inventory.cargo_wagon).set_filter(i, stored_info.filters[i]) + end + end + -- sets inventory limit + if stored_info.bar then + entity.get_inventory(defines.inventory.cargo_wagon).set_bar(stored_info.bar) + end + -- sets train to automatic mode again, when train is restored completely + local old_train = stored_info.train + if settings.global["ick-automatic-mode"].value and old_train then + local new_train = {["locomotive"] = 0, ["cargo-wagon"] = 0, ["fluid-wagon"] = 0, ["artillery-wagon"] = 0} + for _, entity in pairs(entity.train.carriages) do + new_train[entity.type] = new_train[entity.type] + 1 + end + if old_train["locomotive"] == new_train["locomotive"] and old_train["cargo-wagon"] == new_train["cargo-wagon"] and old_train["fluid-wagon"] == new_train["fluid-wagon"] and old_train["artillery-wagon"] == new_train["artillery-wagon"] then + entity.train.manual_mode = false + end + end + end + end + end +end + +-- called when a player builds the ghost entity +script.on_event(defines.events.on_built_entity, function(event) + built_entity(event.created_entity) +end, {{filter = "rolling-stock"}}) + +-- called when a robot builds the ghost entity +script.on_event(defines.events.on_robot_built_entity, function(event) + built_entity(event.created_entity) +end, {{filter = "rolling-stock"}}) + + +-- ON ENTITY DESTROYED +-- called when a registerd entity gets removed (rolling stock ghosts or item request proxies) +script.on_event(defines.events.on_entity_destroyed, function(event) + if global.ick_destroyed_train and global.ick_destroyed_train[event.registration_number] then + -- moves equipment from cargo inventory into grid + local registerd_entity = global.ick_destroyed_train[event.registration_number] + if registerd_entity.requests and registerd_entity.target then + local inventory = registerd_entity.target.get_inventory(defines.inventory.cargo_wagon) + local grid = registerd_entity.target.grid + for name, count in pairs(registerd_entity.requests) do + if inventory and grid then + for i = 1, count do + local stack = inventory.find_item_stack(name) + if stack and grid.put{name = name} then + stack.count = stack.count - 1 + end + end + end + end + end + -- deletes saved inventory properties + global.ick_destroyed_train[event.registration_number] = nil + end +end) diff --git a/info.json b/info.json new file mode 100644 index 0000000..5549e20 --- /dev/null +++ b/info.json @@ -0,0 +1,11 @@ +{ + "name": "ick-automatic-train-repair", + "version": "1.1.0", + "title": "Automatic train repair", + "author": "ickputzdirwech", + "contact": "ickputzdirwech@gmx.de", + "homepage": "github.com/ickputzdirwech/ick-automatic-train-repair", + "factorio_version": "1.1", + "dependencies":["base >= 1.1.33"], + "description": "If any locomotive or wagon is destroyed the train is stopped and a ghost is created. Including inventory filters and inventory limit, fuel and equipment. As soon as the train is fully repaired it is set to automatic mode again." +} diff --git a/locale/en/ick.cfg b/locale/en/ick.cfg new file mode 100644 index 0000000..234fe9b --- /dev/null +++ b/locale/en/ick.cfg @@ -0,0 +1,10 @@ +[mod-setting-name] +ick-automatic-mode=Automatically enable automatic mode +ick-include-equipment=Create item requests for destroyed equipment +ick-include-fuel=Create item requests for fuel +ick-fuel-type=Name of the fuel that should be requested +ick-fuel-amount=How much of the fuel should be requested + +[mod-setting-description] +ick-automatic-mode=Trains will be set to automatic mode again as soon as all entity ghosts were built. +ick-fuel-type=If left empty the destroyed fuel will be requested. diff --git a/settings.lua b/settings.lua new file mode 100644 index 0000000..f702919 --- /dev/null +++ b/settings.lua @@ -0,0 +1,41 @@ +data:extend( +{ + { + setting_type = "runtime-global", + type = "bool-setting", + name = "ick-automatic-mode", + order = "a[automatic-mode]", + default_value = true + }, + { + setting_type = "runtime-global", + type = "bool-setting", + name = "ick-include-equipment", + order = "b[include-equipment]", + default_value = true + }, + { + setting_type = "runtime-global", + type = "bool-setting", + name = "ick-include-fuel", + order = "c[ick-include-fuel]", + default_value = true + }, + { + setting_type = "runtime-global", + type = "string-setting", + name = "ick-fuel-type", + order = "d[fuel]", + allow_blank = true, + auto_trim = true, + default_value = "" + }, + { + setting_type = "runtime-global", + type = "int-setting", + name = "ick-fuel-amount", + order = "e[fuel-amount]", + minimum_value = 1, + default_value = 1 + }, +})