509 lines
17 KiB
Lua

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