local upgrade_planner_entity_upgrade = {} local function create_new_entity_data(player, old_entity, new_entity_prototype) local surface = old_entity.surface player.cursor_stack.set_stack {name = "blueprint", count = 1} player.cursor_stack.create_blueprint { surface = surface, force = old_entity.force, area = old_entity.bounding_box, } local old_width = old_entity.bounding_box.right_bottom.x - old_entity.bounding_box.left_top.x local old_height = old_entity.bounding_box.right_bottom.y - old_entity.bounding_box.left_top.y local new_width = new_entity_prototype.collision_box.right_bottom.x - new_entity_prototype.collision_box.left_top.x local new_height = new_entity_prototype.collision_box.right_bottom.y - new_entity_prototype.collision_box.left_top.y if old_entity.direction == 2 or old_entity.direction == 6 then new_width, new_height = new_height, new_width end local diff_width = old_width - new_width local diff_height = old_height - new_height local new_x = old_entity.position.x local new_y = old_entity.position.y if diff_height > 0 and ((diff_height % 2) > 0.5 and (diff_height % 2) < 1.5) then new_y = new_y - 1 end if diff_width > 0 and ((diff_width % 2) > 0.5 and (diff_width % 2) < 1.5) then new_x = new_x - 1 end local new_entity_data = player.cursor_stack.get_blueprint_entities()[1] new_entity_data.name = new_entity_prototype.name new_entity_data.position = {x = new_x, y = new_y} new_entity_data.force = old_entity.force new_entity_data.direction = old_entity.direction new_entity_data.player = player new_entity_data.spill = false player.cursor_stack.set_stack {name = "upgrade-builder", count = 1} return new_entity_data end local function get_hashmap(config) local items = game.item_prototypes local hashmap = {} for k, entry in pairs(config) do local item_from = items[entry.from] local item_to = items[entry.to] if item_to and item_from then hashmap[entry.from] = {item_to = entry.to} local entity_from = item_from.place_result local entity_to = item_to.place_result if entity_from and entity_to then hashmap[entity_from.name] = { entity_to = entity_to.name, item_to = entry.to, item_from = entry.from, } end if item_from.type == "rail-planner" and item_to.type == "rail-planner" then hashmap[item_from.straight_rail.name] = { entity_to = item_to.straight_rail.name, item_to = entry.to, item_from = entry.from, } hashmap[item_from.curved_rail.name] = { entity_to = item_to.curved_rail.name, item_to = entry.to, item_from = entry.from, item_amount = 4, } end end end return hashmap end upgrade_planner_entity_upgrade.get_hashmap = get_hashmap local function get_recipe(owner) local recipe if not owner.valid then return end if owner.type == "beacon" then recipe = game.recipe_prototypes["stone-furnace"] -- Some dummy recipe to get correct limitation elseif owner.type == "assembling-machine" or owner.type == "furnace" then recipe = owner.get_recipe() or "iron-gear-wheel" end return recipe end local function check_module_eligibility(name, recipe) if not recipe then return true end local item = game.item_prototypes[name] if not item then return false end local effects = item.module_effects if not effects then return true end if not effects.productivity then return true end if not item.limitations then return true end for _, limitation in pairs(item.limitations) do if limitation == recipe.name then return true end end return false end local function player_upgrade_modules(player, inventory, map, owner) for k = 1, #inventory do local slot = inventory[k] if slot.valid and slot.valid_for_read then if not global.temporary_ignore[slot.name] then local upgrade = map[slot.name] local recipe = get_recipe(owner) if upgrade and upgrade.item_to and recipe and check_module_eligibility(upgrade.item_to, recipe) then if player.get_item_count(upgrade.item_to) >= slot.count or player.cheat_mode then player.remove_item {name = upgrade.item_to, count = slot.count} player.insert {name = slot.name, count = slot.count} slot.set_stack {name = upgrade.item_to, count = slot.count} else global.temporary_ignore[slot.name] = true owner.surface.create_entity { name = "flying-text", position = {owner.position.x - 1.3, owner.position.y - 0.5}, text = {"upgrade-planner.insufficient-items"}, color = {r = 1, g = 0.6, b = 0.6}, } end end end end end end local function player_upgrade(player, old_entity, upgrade, upgrade_neighbours) if not old_entity then return end if old_entity.to_be_deconstructed() then return end if not upgrade.entity_to then log("Tried to upgrade when entry had no entity: " .. serpent.line(upgrade)) return end if global.temporary_ignore[old_entity.name] then return end local surface = old_entity.surface local amount = upgrade.item_amount or 1 if player.get_item_count(upgrade.item_to) >= amount or player.cheat_mode then local new_entity local new_entity_prototype if string.find(upgrade.item_to, "miniloader") then local prototype_to = upgrade.item_to .. '-inserter' new_entity_prototype = game.entity_prototypes[prototype_to] else new_entity_prototype = game.entity_prototypes[upgrade.item_to] end local new_entity_data = create_new_entity_data(player, old_entity, new_entity_prototype) local insert_item = false -- script.raise_event(defines.events.on_pre_player_mined_item, -- {player_index = player.index, entity = old_entity}) new_entity_data.fast_replace = true new_entity_data.create_build_effect_smoke = false if old_entity.type == "inserter" then local drop = { x = old_entity.drop_position.x, y = old_entity.drop_position.y, } local pickup = { x = old_entity.pickup_position.x, y = old_entity.pickup_position.y, } new_entity = surface.create_entity(new_entity_data) if new_entity.valid and new_entity.type == "inserter" then new_entity.pickup_position = pickup new_entity.drop_position = drop end else if old_entity.type == "underground-belt" then if old_entity.neighbours and upgrade_neighbours then player_upgrade(player, old_entity.neighbours, upgrade, false) end end new_entity = surface.create_entity(new_entity_data) end new_entity_data.fast_replace = false if old_entity.valid then if old_entity.type == "straight-rail" or old_entity.type == "curved-rail" then old_entity.destroy() new_entity = surface.create_entity { name = upgrade.entity_to, position = old_entity.position, force = old_entity.force, direction = old_entity.direction, } end end if old_entity.valid then if new_entity and new_entity.valid then new_entity.destroy() end local a = old_entity.bounding_box -- Get current entity data and copy other values player.cursor_stack.set_stack {name = "blueprint", count = 1} player.cursor_stack.create_blueprint { surface = surface, force = old_entity.force, area = a, } local entity_data = player.cursor_stack.get_blueprint_entities()[1] entity_data.position = old_entity.position entity_data.force = old_entity.force entity_data.direction = old_entity.direction entity_data.player = player player.cursor_stack.set_stack {name = "upgrade-builder", count = 1} -- Stash inventories for later distribution local inventories = {} for index = 1, 10 do if old_entity.get_inventory(index) ~= nil then inventories[index] = {} inventories[index].name = index inventories[index].contents = old_entity.get_inventory(index) .get_contents() end end old_entity.destroy() -- Check if new entity can be placed, otherwise recreate the old one if surface.can_place_entity(new_entity_data) then new_entity = surface.create_entity(new_entity_data) insert_item = true else new_entity = surface.create_entity(entity_data) player.create_local_flying_text { text = {"upgrade-planner.upgrade-placement-blocked"}, position = entity_data.position, color = {r = 1, g = 0, b = 0}, } end -- Redistribute inventories for j, items in pairs(inventories) do for item, count in pairs(items.contents) do if new_entity ~= nil then local inv = new_entity.get_inventory(items.name) if inv then inv.insert {name = item, count = count} else local num = player.insert {name = item, count = count} if num < count then player.surface.spill_item_stack(player.position, { name = item, count = (count - num), }, true, nil, false) end end end end end local proxy = surface.find_entities_filtered { area = a, name = "item-request-proxy", } if proxy[1] ~= nil then proxy[1].destroy() end end -- Insert items not replaced with fast-replace if insert_item then local inser_cnt = player.insert {name = upgrade.item_from, count = amount} if inser_cnt < amount then player.surface.spill_item_stack(player.position, { name = upgrade.item_from, count = (amount - inser_cnt), }, true, nil, false) end end -- Raise appropriate events player.remove_item {name = upgrade.item_to, count = amount} -- script.raise_event(defines.events.on_player_mined_item, { -- player_index = player.index, -- item_stack = {name = upgrade.item_from, count = 1}, -- }) -- script.raise_event(defines.events.on_built_entity, { -- player_index = player.index, -- created_entity = new_entity, -- stack = player.cursor_stack, -- }) else global.temporary_ignore[old_entity.name] = true surface.create_entity { name = "flying-text", position = {old_entity.position.x - 1.3, old_entity.position.y - 0.5}, text = {"upgrade-planner.insufficient-items"}, color = {r = 1, g = 0.6, b = 0.6}, } end end upgrade_planner_entity_upgrade.upgrade_area_player = function(event) if event.item ~= "upgrade-builder" then return end -- If its a upgrade builder local player = game.players[event.player_index] local config = global.current_config[player.index] if config == nil then return end local hashmap = get_hashmap(config) global.temporary_ignore = {} for k, entity in pairs(event.entities) do -- Get the items that are set to be upgraded if entity.valid then local upgrade = hashmap[entity.name] if entity.get_module_inventory() then player_upgrade_modules(player, entity.get_module_inventory(), hashmap, entity) end if upgrade ~= nil and upgrade ~= "" then player_upgrade(player, entity, upgrade, true) end end end global.temporary_ignore = nil end local function robot_upgrade_modules(inventory, map, owner) if not owner then return end if not owner.valid then return end local surface = owner.surface local modules = {} local proxy = false for k = 1, #inventory do local slot = inventory[k] if slot.valid and slot.valid_for_read then local upgrade = map[slot.name] local recipe = get_recipe(owner) if upgrade and upgrade.item_to and recipe and check_module_eligibility(upgrade.item_to, recipe) then local entity = surface.create_entity { name = "item-on-ground", stack = {name = slot.name, count = slot.count}, position = owner.position, force = owner.force, } entity.order_deconstruction(owner.force) if modules[upgrade.item_to] then modules[upgrade.item_to] = modules[upgrade.item_to] + slot.count else modules[upgrade.item_to] = slot.count end proxy = true slot.clear() end end end if proxy then surface.create_entity { name = "item-request-proxy", force = owner.force, position = owner.position, modules = modules, target = owner, } end end upgrade_planner_entity_upgrade.upgrade_area_bot = function(event) -- this is a lot simpler... but less cool if event.item ~= "upgrade-builder" then return end local player = game.players[event.player_index] local config = global.current_config[player.index] if not config then return end local hashmap = get_hashmap(config) for k, entity in pairs(event.entities) do if entity.valid then local upgrade = hashmap[entity.name] if upgrade and upgrade ~= "" then entity.order_upgrade({ force = entity.force, target = upgrade["entity_to"], player = player, }) end if entity.valid and entity.get_module_inventory() then robot_upgrade_modules(entity.get_module_inventory(), hashmap, entity) end end end end local function update_blueprint_entities(stack, hashmap) if not (stack and stack.valid and stack.valid_for_read and stack.is_blueprint_setup()) then return end local entities = stack.get_blueprint_entities() if entities then for k, entity in pairs(entities) do local new_entity = hashmap[entity.name] if new_entity and new_entity.entity_to then entities[k].name = new_entity.entity_to end if entity.items then local new_items = {} for item, count in pairs(entity.items) do new_items[item] = count end for item, count in pairs(entity.items) do local new = hashmap[item] if new and new.item_to then if new_items[new.item_to] then new_items[new.item_to] = new_items[new.item_to] + count else new_items[new.item_to] = count end new_items[item] = new_items[item] - count end end for item, count in pairs(new_items) do if count == 0 then new_items[item] = nil end end entities[k].items = new_items end end stack.set_blueprint_entities(entities) end local tiles = stack.get_blueprint_tiles() if tiles then local tile_prototypes = game.tile_prototypes local items = game.item_prototypes for k, tile in pairs(tiles) do local prototype = tile_prototypes[tile.name] local items_to_place = prototype.items_to_place_this local item = nil if items_to_place then for _, item_stack in pairs(items_to_place) do item = hashmap[item_stack.name] if item and item.item_to then break end end end if item then local tile_item = items[item.item_to] if tile_item then local result = tile_item.place_as_tile_result if result then local new_tile = tile_prototypes[result.result.name] if new_tile and new_tile.can_be_part_of_blueprint then tiles[k].name = result.result.name end end end end end stack.set_blueprint_tiles(tiles) end local icons = stack.blueprint_icons for k, icon in pairs(icons) do local new = hashmap[icon.signal.name] if new and new.item_to then icons[k].signal.name = new.item_to end end stack.blueprint_icons = icons return true end upgrade_planner_entity_upgrade.upgrade_blueprint = function(event) local player = game.players[event.player_index] local stack = player.cursor_stack if not (stack.valid and stack.valid_for_read) then return end local config = global.current_config[player.index] if not config then return end local hashmap = get_hashmap(config) if stack.is_blueprint then if update_blueprint_entities(stack, hashmap) then player.print({"upgrade-planner.blueprint-upgrade-successful"}) end return end if stack.is_blueprint_book then local inventory = stack.get_inventory(defines.inventory.item_main) local success = 0 for k = 1, #inventory do if update_blueprint_entities(inventory[k], hashmap) then success = success + 1 end end player.print({ "upgrade-planner.blueprint-book-upgrade-successful", success, }) return end end return upgrade_planner_entity_upgrade