MAX_CONFIG_SIZE = 10 MAX_STORAGE_SIZE = 12 function glob_init() global["entity-recipes"] = global["entity-recipes"] or {} global["config"] = global["config"] or {} global["config-tmp"] = global["config-tmp"] or {} global["storage"] = global["storage"] or {} end function get_type(entity) if entity then if game.entity_prototypes[entity] then return game.entity_prototypes[entity].type end end return "" end function count_keys(hashmap) local result = 0 for _, __ in pairs(hashmap) do result = result + 1 end return result end function get_config_item(player, index, type) if not global["config-tmp"][player.name] or index > #global["config-tmp"][player.name] or global["config-tmp"][player.name][index][type] == "" then return {"upgrade-planner-item-not-set"} end return game.item_prototypes[global["config-tmp"][player.name][index][type]].localised_name end function gui_init(player, after_research) if player.gui.top["replacer-config-button"] then player.gui.top["replacer-config-button"].destroy() end if not player.gui.top["upgrade-planner-config-button"] and (player.force.technologies["upgrade-builder"].researched or after_research) then player.gui.top.add{ type = "button", name = "upgrade-planner-config-button", style = "upgrade-planner-menu-button" } end end function gui_open_frame(player) local frame = player.gui.left["upgrade-planner-config-frame"] local storage_frame = player.gui.left["upgrade-planner-storage-frame"] if frame then frame.destroy() if storage_frame then storage_frame.destroy() end global["config-tmp"][player.name] = nil return end -- If player config does not exist, we need to create it. global["config"][player.name] = global["config"][player.name] or {} -- Temporary config lives as long as the frame is open, so it has to be created -- every time the frame is opened. global["config-tmp"][player.name] = {} -- We need to copy all items from normal config to temporary config. local i = 0 for i = 1, MAX_CONFIG_SIZE do if i > #global["config"][player.name] then global["config-tmp"][player.name][i] = { from = "", to = "" } else global["config-tmp"][player.name][i] = { from = global["config"][player.name][i].from, to = global["config"][player.name][i].to } end end -- Now we can build the GUI. frame = player.gui.left.add{ type = "frame", caption = {"upgrade-planner-config-frame-title"}, name = "upgrade-planner-config-frame", direction = "vertical" } local error_label = frame.add{ type = "label", caption = "---", name = "upgrade-planner-error-label" } error_label.style.minimal_width = 200 local ruleset_grid = frame.add{ type = "table", colspan = 3, name = "upgrade-planner-ruleset-grid" } ruleset_grid.add{ type = "label", name = "upgrade-planner-grid-header-1", caption = {"upgrade-planner-config-header-1"} } ruleset_grid.add{ type = "label", name = "upgrade-planner-grid-header-2", caption = {"upgrade-planner-config-header-2"} } ruleset_grid.add{ type = "label", name = "upgrade-planner-grid-header-3", caption = "" } for i = 1, MAX_CONFIG_SIZE do ruleset_grid.add{ type = "button", name = "upgrade-planner-from-" .. i, style = "upgrade-planner-small-button", caption = get_config_item(player, i, "from") } ruleset_grid.add{ type = "button", name = "upgrade-planner-to-" .. i, style = "upgrade-planner-small-button", caption = get_config_item(player, i, "to") } ruleset_grid.add{ type = "button", name = "upgrade-planner-clear-" .. i, style = "upgrade-planner-small-button", caption = {"upgrade-planner-config-button-clear"} } end local button_grid = frame.add{ type = "table", colspan = 2, name = "upgrade-planner-button-grid" } button_grid.add{ type = "button", name = "upgrade-planner-apply", caption = {"upgrade-planner-config-button-apply"} } button_grid.add{ type = "button", name = "upgrade-planner-clear-all", caption = {"upgrade-planner-config-button-clear-all"} } storage_frame = player.gui.left.add{ type = "frame", name = "upgrade-planner-storage-frame", caption = {"upgrade-planner-storage-frame-title"}, direction = "vertical" } local storage_frame_error_label = storage_frame.add{ type = "label", name = "upgrade-planner-storage-error-label", caption = "---" } storage_frame_error_label.style.minimal_width = 200 local storage_frame_buttons = storage_frame.add{ type = "table", colspan = 3, name = "upgrade-planner-storage-buttons" } storage_frame_buttons.add{ type = "label", caption = {"upgrade-planner-storage-name-label"}, name = "upgrade-planner-storage-name-label" } storage_frame_buttons.add{ type = "textfield", text = "", name = "upgrade-planner-storage-name" } storage_frame_buttons.add{ type = "button", caption = {"upgrade-planner-storage-store"}, name = "upgrade-planner-storage-store", style = "upgrade-planner-small-button" } local storage_grid = storage_frame.add{ type = "table", colspan = 3, name = "upgrade-planner-storage-grid" } if global["storage"][player.name] then i = 1 for key, _ in pairs(global["storage"][player.name]) do storage_grid.add{ type = "label", caption = key .. " ", name = "upgrade-planner-storage-entry-" .. i } storage_grid.add{ type = "button", caption = {"upgrade-planner-storage-restore"}, name = "upgrade-planner-restore-" .. i, style = "upgrade-planner-small-button" } storage_grid.add{ type = "button", caption = {"upgrade-planner-storage-remove"}, name = "upgrade-planner-remove-" .. i, style = "upgrade-planner-small-button" } i = i + 1 end end end function gui_save_changes(player) -- Saving changes consists in: -- 1. copying config-tmp to config -- 2. removing config-tmp -- 3. closing the frame if global["config-tmp"][player.name] then local i = 0 global["config"][player.name] = {} for i = 1, #global["config-tmp"][player.name] do -- Rule can be saved only if both "from" and "to" fields are set. if global["config-tmp"][player.name][i].from == "" or global["config-tmp"][player.name][i].to == "" then global["config"][player.name][i] = { from = "", to = "" } else global["config"][player.name][i] = { from = global["config-tmp"][player.name][i].from, to = global["config-tmp"][player.name][i].to } end end global["config-tmp"][player.name] = nil end local frame = player.gui.left["upgrade-planner-config-frame"] local storage_frame = player.gui.left["upgrade-planner-storage-frame"] if frame then frame.destroy() if storage_frame then storage_frame.destroy() end end end function gui_clear_all(player) local i = 0 local frame = player.gui.left["upgrade-planner-config-frame"] if not frame then return end local ruleset_grid = frame["upgrade-planner-ruleset-grid"] for i = 1, MAX_CONFIG_SIZE do global["config-tmp"][player.name][i] = { from = "", to = "" } ruleset_grid["upgrade-planner-from-" .. i].caption = {"upgrade-planner-item-not-set"} ruleset_grid["upgrade-planner-to-" .. i].caption = {"upgrade-planner-item-not-set"} end end function gui_display_message(frame, storage, message) local label_name = "upgrade-planner-" if storage then label_name = label_name .. "storage-" end label_name = label_name .. "error-label" local error_label = frame[label_name] if not error_label then return end if message ~= "---" then message = {message} end error_label.caption = message end function gui_set_rule(player, type, index) local frame = player.gui.left["upgrade-planner-config-frame"] if not frame or not global["config-tmp"][player.name] then return end local stack = player.cursor_stack if not stack.valid_for_read then gui_display_message(frame, false, "upgrade-planner-item-empty") return end if stack.name ~= "deconstruction-planner" or type ~= "to" then local opposite = "from" local i = 0 if type == "from" then opposite = "to" for i = 1, #global["config-tmp"][player.name] do if index ~= i and global["config-tmp"][player.name][i].from == stack.name then gui_display_message(frame, false, "upgrade-planner-item-already-set") return end end end local related = global["config-tmp"][player.name][index][opposite] if related ~= "" then if related == stack.name then gui_display_message(frame, false, "upgrade-planner-item-is-same") return end if get_type(item_name_to_entity_name(stack.name)) ~= get_type(related) then gui_display_message(frame, false, "upgrade-planner-item-not-same-type") return end end end global["config-tmp"][player.name][index][type] = stack.name local ruleset_grid = frame["upgrade-planner-ruleset-grid"] ruleset_grid["upgrade-planner-" .. type .. "-" .. index].caption = game.item_prototypes[stack.name].localised_name end function gui_clear_rule(player, index) local frame = player.gui.left["upgrade-planner-config-frame"] if not frame or not global["config-tmp"][player.name] then return end gui_display_message(frame, false, "---") local ruleset_grid = frame["upgrade-planner-ruleset-grid"] global["config-tmp"][player.name][index] = { from = "", to = "" } ruleset_grid["upgrade-planner-from-" .. index].caption = {"upgrade-planner-item-not-set"} ruleset_grid["upgrade-planner-to-" .. index].caption = {"upgrade-planner-item-not-set"} end function gui_store(player) global["storage"][player.name] = global["storage"][player.name] or {} local storage_frame = player.gui.left["upgrade-planner-storage-frame"] if not storage_frame then return end local textfield = storage_frame["upgrade-planner-storage-buttons"]["upgrade-planner-storage-name"] local name = textfield.text name = string.match(name, "^%s*(.-)%s*$") if not name or name == "" then gui_display_message(storage_frame, true, "upgrade-planner-storage-name-not-set") return end if global["storage"][player.name][name] then gui_display_message(storage_frame, true, "upgrade-planner-storage-name-in-use") return end global["storage"][player.name][name] = {} local i = 0 for i = 1, #global["config-tmp"][player.name] do global["storage"][player.name][name][i] = { from = global["config-tmp"][player.name][i].from, to = global["config-tmp"][player.name][i].to } end local storage_grid = storage_frame["upgrade-planner-storage-grid"] local index = count_keys(global["storage"][player.name]) + 1 if index > MAX_STORAGE_SIZE + 1 then gui_display_message(storage_frame, true, "upgrade-planner-storage-too-long") return end storage_grid.add{ type = "label", caption = name .. " ", name = "upgrade-planner-storage-entry-" .. index } storage_grid.add{ type = "button", caption = {"upgrade-planner-storage-restore"}, name = "upgrade-planner-restore-" .. index, style = "upgrade-planner-small-button" } storage_grid.add{ type = "button", caption = {"upgrade-planner-storage-remove"}, name = "upgrade-planner-remove-" .. index, style = "upgrade-planner-small-button" } gui_display_message(storage_frame, true, "---") textfield.text = "" end function gui_restore(player, index) local frame = player.gui.left["upgrade-planner-config-frame"] local storage_frame = player.gui.left["upgrade-planner-storage-frame"] if not frame or not storage_frame then return end local storage_grid = storage_frame["upgrade-planner-storage-grid"] local storage_entry = storage_grid["upgrade-planner-storage-entry-" .. index] if not storage_entry then return end local name = string.match(storage_entry.caption, "^%s*(.-)%s*$") if not global["storage"][player.name] or not global["storage"][player.name][name] then return end global["config-tmp"][player.name] = {} local i = 0 local ruleset_grid = frame["upgrade-planner-ruleset-grid"] for i = 1, MAX_CONFIG_SIZE do if i > #global["storage"][player.name][name] then global["config-tmp"][player.name][i] = { from = "", to = "" } else global["config-tmp"][player.name][i] = { from = global["storage"][player.name][name][i].from, to = global["storage"][player.name][name][i].to } end ruleset_grid["upgrade-planner-from-" .. i].caption = get_config_item(player, i, "from") ruleset_grid["upgrade-planner-to-" .. i].caption = get_config_item(player, i, "to") end gui_display_message(storage_frame, true, "---") end function gui_remove(player, index) if not global["storage"][player.name] then return end local storage_frame = player.gui.left["upgrade-planner-storage-frame"] if not storage_frame then return end local storage_grid = storage_frame["upgrade-planner-storage-grid"] local label = storage_grid["upgrade-planner-storage-entry-" .. index] local btn1 = storage_grid["upgrade-planner-restore-" .. index] local btn2 = storage_grid["upgrade-planner-remove-" .. index] if not label or not btn1 or not btn2 then return end local name = string.match(label.caption, "^%s*(.-)%s*$") label.destroy() btn1.destroy() btn2.destroy() global["storage"][player.name][name] = nil gui_display_message(storage_frame, true, "---") end function item_name_to_prototype(name) --or nil if name then return game.item_prototypes[name] end end function item_prototype_to_entity_prototype (item) --or nil if item and item.place_result then return item.place_result end end function item_name_to_entity_name(name) local entity = item_prototype_to_entity_prototype(item_name_to_prototype(name)) if entity then return entity.name end end function signal_to_item_name(signal) --or nil if signal and signal.type == "item" then return signal.name end end function get_upgrade_for_item_name(config,name) local index = 0 for i = 1, #config do if config[i].from == name then index = i break end end if index>0 then return config[index].to end end function get_upgrade_for_entity_name(config,name) if name then local index = 0 for i = 1, #config do if item_name_to_entity_name(config[i].from) == name then index = i break end end if index>0 then return config[index].to end end end function upgrade_blueprint(player, blueprint) if not blueprint.is_blueprint_setup() then return end local config = global["config"][player.name] if config ~= nil then local bluedata=blueprint.get_blueprint_entities() for i=#bluedata,1,-1 do local belt=bluedata[i] local upgrade = get_upgrade_for_entity_name(config,belt.name) if upgrade ~= nil then if upgrade ~="deconstruction-planner" then belt.name=item_name_to_entity_name(upgrade) else table.remove(bluedata, i) end end end -- add tile upgrades here! blueprint.set_blueprint_entities(bluedata) if(not blueprint.is_blueprint_setup()) then return end local icons=blueprint.blueprint_icons for i = 1,#icons do local icon=icons[i] local item_name = signal_to_item_name(icon.signal) local upgrade = get_upgrade_for_item_name(config,item_name) if upgrade then if upgrade ~="deconstruction-planner" then icon.signal.name=upgrade else icon.signal.name=nil end end end blueprint.blueprint_icons=icons if #blueprint.blueprint_icons == 0 then blueprint.blueprint_icons=blueprint.default_icons end end end script.on_event(defines.events.on_gui_click, function(event) local element = event.element local player = game.players[event.player_index] if element.name == "upgrade-planner-config-button" then if(player.cursor_stack.valid_for_read and player.cursor_stack.type == "blueprint") then upgrade_blueprint(player,player.cursor_stack) else gui_open_frame(player) end elseif element.name == "upgrade-planner-apply" then gui_save_changes(player) elseif element.name == "upgrade-planner-clear-all" then gui_clear_all(player) elseif element.name == "upgrade-planner-storage-store" then gui_store(player) else local type, index = string.match(element.name, "upgrade%-planner%-(%a+)%-(%d+)") if type and index then if type == "from" or type == "to" then gui_set_rule(player, type, tonumber(index)) elseif type == "restore" then gui_restore(player, tonumber(index)) elseif type == "remove" then gui_remove(player, tonumber(index)) elseif type == "clear" then gui_clear_rule(player, tonumber(index)) end end end end) script.on_event(defines.events.on_research_finished, function(event) if event.research.name == 'upgrade-builder' then for _, player in pairs(game.players) do if player.force == event.research.force then gui_init(player, true) end end end end) script.on_init(function() glob_init() for _, player in pairs(game.players) do gui_init(player, false) end end) script.on_event(defines.events.on_player_selected_area, function(event) if event.item == "upgrade-builder" then local player = game.players[event.player_index] local config = global["config"][player.name] if config ~= nil then local surface = player.surface for k, belt in pairs (event.entities) do local upgrade = get_upgrade_for_entity_name(config,belt.name) if upgrade ~= nil then if player.get_item_count(upgrade) > 0 or Cheat_mode then local upgrade_entity=item_prototype_to_entity_prototype(item_name_to_prototype(upgrade)) if upgrade_entity==nil then -- what to do here? -- we are upgrading to an item that does not create an entity. -- For now we do nothing. else local d = belt.direction local f = belt.force local p = belt.position local n = belt.name if player.can_reach_entity(belt) then if upgrade ~="deconstruction-planner" then if belt.type == "underground-belt" then surface.create_entity { player = player, name = upgrade_entity.name, position = belt.position, force = belt.force, fast_replace = true, direction = belt.direction, type = belt.belt_to_ground_type, spill=false } else surface.create_entity { player = player, name = upgrade_entity.name, position = belt.position, force = belt.force, fast_replace = true, direction = belt.direction, spill=false } end if belt.valid then local a = {{p.x-0.5,p.y-0.5},{p.x+0.5,p.y+0.5}} player.cursor_stack.set_stack{name = "blueprint", count = 1} player.cursor_stack.create_blueprint{surface = surface, force = belt.force,area = a} local old_blueprint = player.cursor_stack.get_blueprint_entities() old_blueprint[1].name = upgrade_entity.name player.cursor_stack.set_stack{name = "blueprint", count = 1} player.cursor_stack.set_blueprint_entities(old_blueprint) --fixme should maintain health, and also use mineable_properties player.insert{name = belt.name, count = 1} inventories = {} for index = 1,10 do if belt.get_inventory(index) ~= nil then inventories[index] = {} inventories[index].name = index inventories[index].contents = belt.get_inventory(index).get_contents() end end belt.destroy() player.cursor_stack.build_blueprint{surface = surface, force = f, position = p} local ghost = surface.find_entities_filtered{area = a, name = "entity-ghost"} if ghost[1]== nil then player.remove_item{name = upgrade, count = 1} else -- this code is strange. It seems to be moving the player away from the ghost. local check = ghost[1].revive() if check == nil then player.teleport{player.position.x -(ghost[1].position.x - player.position.x),player.position.y -(ghost[1].position.y - player.position.y)} check = ghost[1].revive() if check == nil then player.teleport{player.position.x -(ghost[1].position.x - player.position.x),player.position.y -(ghost[1].position.y - player.position.y)} check = ghost[1].revive() if check == nil then player.teleport{player.position.x -(ghost[1].position.x - player.position.x),player.position.y -(ghost[1].position.y - player.position.y)} check = ghost[1].revive() end end player.teleport(surface.find_non_colliding_position("player",player.position,3, 0.1)) end if check ~= nil then player.remove_item{name = upgrade, count = 1} end end local assembling = surface.find_entities_filtered{area = a, name = upgrade_entity.name} for j, items in pairs (inventories) do for l, contents in pairs (items.contents) do if assembling[1] ~= nil then assembling[1].get_inventory(items.name).insert{name = l, count = contents} end end end inventories = nil local proxy = surface.find_entities_filtered{area = a, name = "item-request-proxy"} if proxy[1]~= nil then proxy[1].destroy() end player.cursor_stack.set_stack{name = "upgrade-builder", count = 1} else player.remove_item{name = upgrade, count = 1} end else --this is wrong should look at mining properties and should maintain health player.insert{name = n, count = 1} belt.destroy() end else surface.create_entity{name = "flying-text", position = {belt.position.x-1.3,belt.position.y-0.5}, text = "Out of range",color = {r=1,g=0.6,b=0.6}} end end else surface.create_entity{name = "flying-text", position = {belt.position.x-1.3,belt.position.y-0.5}, text = "Insufficient items", color = {r=1,g=0.6,b=0.6}} end end end local index = 0 for i = 1, #config do if config[i].from == "raw-wood" then index = i break end end if index > 0 then local area = event.area -- if area is so small that it rounds to zero, skip rest of function. if not pcall(function() player.surface.find_entities_filtered({area = area, type = "tree"}) end) then return end for k, fucking_tree in pairs (player.surface.find_entities_filtered({area = area, type = "tree"})) do if player.can_reach_entity(fucking_tree) then if player.can_insert({name= "raw-wood", count = fucking_tree.prototype.mineable_properties.amount_max}) then player.insert({name= "raw-wood", count = fucking_tree.prototype.mineable_properties.amount_max}) fucking_tree.die() else player.print("Cannot insert raw wood") break end else player.surface.create_entity{name = "flying-text", position = {fucking_tree.position.x-1.3,fucking_tree.position.y-0.5}, text = "Out of range",color = {r=1,g=0.6,b=0.6}} end end end end end end ) script.on_event(defines.events.on_player_alt_selected_area, function(event) if event.item == "upgrade-builder" then local player = game.players[event.player_index] local config = global["config"][player.name] if config ~= nil then local surface = player.surface for k, belt in pairs (event.entities) do local upgrade = get_upgrade_for_entity_name(config,belt.name) if upgrade ~= nil then local p = belt.position local d = belt.direction local f = belt.force local p = belt.position local a = {{p.x-0.5,p.y-0.5},{p.x+0.5,p.y+0.5}} if upgrade ~="deconstruction-planner" then local upgrade_entity = item_name_to_entity_name(upgrade) if upgrade_entity ~= nil then player.cursor_stack.set_stack{name = "blueprint", count = 1} player.cursor_stack.create_blueprint{surface = surface, force = belt.force,area = a} local old_blueprint = player.cursor_stack.get_blueprint_entities() old_blueprint[1].name = upgrade_entity player.cursor_stack.set_stack{name = "blueprint", count = 1} player.cursor_stack.set_blueprint_entities(old_blueprint) belt.order_deconstruction(f) player.cursor_stack.build_blueprint{surface = surface, force = f, position = p} player.cursor_stack.set_stack{name = "upgrade-builder", count = 1} end else belt.order_deconstruction(f) end end end --And now support deconstructing trees... local index = 0 for i = 1, #config do if config[i].from == "raw-wood" then index = i break end end if index > 0 then local area = event.area -- if area is so small that it rounds to zero, skip rest of function. if not pcall(function() player.surface.find_entities_filtered({area = area, type = "tree"}) end) then return end for k, fucking_tree in pairs (player.surface.find_entities_filtered({area = area, type = "tree"})) do fucking_tree.order_deconstruction(player.force) end end end end end) script.on_configuration_changed(function(data) if not data or not data.mod_changes then return end if data.mod_changes["upgrade-planner"] then for k, force in pairs (game.forces) do force.reset_recipes() end for k, player in pairs (game.players) do if player.gui.top["upgrade-planner-config-button"] then player.gui.top["upgrade-planner-config-button"].destroy() gui_init(player,true) end end end end)