265 lines
12 KiB
Lua

-------------------------------------------------------------------------------
--[[armormods]] -- Power Armor module code.
-------------------------------------------------------------------------------
local armormods = {}
local table = require('__stdlib__/stdlib/utils/table')
local config = require('config')
--TODO: Store this in global and update in on_con_changed
--TODO: Remote call for inserting/removing into table
local combat_robots = config.COMBAT_ROBOTS
local healer_capsules = config.FOOD
local Position = require('__stdlib__/stdlib/area/position')
local max, abs, ceil, floor = math.max, math.abs, math.ceil, math.floor
--(( Helper functions ))-------------------------------------------------------
-- Loop through equipment grid and return a table of valid equipment tables indexed by equipment name
-- @param entity: the entity object
-- @return table: all equipment - name as key, arrary of named equipment as value
-- @return table: a table of valid equipment with energy in buffer - name as key, array of named equipment as value
-- @return table: shield_level = number, max_shield = number, equipment = array of shields
local function get_valid_equipment(grid)
if grid and grid.valid then
local all, charged, energy_shields = {}, {}, {shield_level = grid.shield, max_shield = grid.max_shield, shields = {}}
for _, equip in pairs(grid.equipment) do
all[equip.name] = all[equip.name] or {}
all[equip.name][#all[equip.name] + 1] = equip
if equip.type == 'energy-shield-equipment' and equip.shield < equip.max_shield * .75 then
energy_shields.shields[#energy_shields.shields + 1] = equip
end
if equip.energy > 0 then
charged[equip.name] = charged[equip.name] or {}
charged[equip.name][#charged[equip.name] + 1] = equip
end
end
return grid, all, charged, energy_shields
end
end
-- Increment the y position for flying text to keep text from overlapping
-- @param position: position table - start position
-- @return function: increments position all subsequent calls
local function increment_position(position)
local x = position.x - 1
local y = position.y - .5
return function()
y = y + 0.5
return {x = x, y = y}
end
end
-- Is the personal roboport ready and have a radius greater than 0
-- @param entity: the entity object
-- @return bool: personal roboport construction radius > 0
local function is_personal_roboport_ready(entity, ignore_radius)
local cell = entity.logistic_cell
return entity.grid and cell and cell.mobile and (cell.construction_radius > 0 or ignore_radius)
end
--TODO .15 will have a better/more reliable way to get the construction network
-- Does the entity have a personal robort and construction robots. Or is in range of a roboport with construction bots.
-- @param entity: the entity object
-- @param mobile_only: bool just return available construction bots in mobile cell
-- @param stationed_only: bool if mobile only return all construction robots
-- @return number: count of available bots
local function get_bot_counts(entity, mobile_only, stationed_only)
if entity.logistic_network then
if mobile_only then
local cell = entity.logistic_cell
if cell and cell.mobile then
if stationed_only then
return cell.stationed_construction_robot_count
else
return cell.logistic_network.available_construction_robots
end
end
else
local bots = 0
table.each(
entity.surface.find_logistic_networks_by_construction_area(entity.position, entity.force),
function(network)
bots = bots + network.available_construction_robots
end
)
return bots
end
else
return 0
end
end
local function get_health_capsules(player)
for name, health in pairs(healer_capsules) do
if game.item_prototypes[name] and player.remove_item({name = name, count = 1}) > 0 then
return max(health, 10), game.item_prototypes[name].localised_name or {'nanobots.free-food-unknown'}
end
end
return 10, {'nanobots.free-food'}
end
local function get_best_follower_capsule(player)
local robot_list = {}
for _, data in ipairs(combat_robots) do
local count = game.item_prototypes[data.capsule] and player.get_item_count(data.capsule) or 0
if count > 0 then
robot_list[#robot_list + 1] = {capsule = data.capsule, unit = data.unit, count = count, qty = data.qty, rank = data.rank}
end
end
return robot_list[1] and robot_list
end
local function get_chip_radius(player, chip_name)
local pdata = global.players[player.index]
local c = player.character
local max_radius = c and c.logistic_cell and c.logistic_cell.mobile and floor(c.logistic_cell.construction_radius) or 15
local custom_radius = pdata.ranges[chip_name] or max_radius
return custom_radius <= max_radius and custom_radius or max_radius
end
--))
--At this point player is valid, not afk and has a character
local function get_chip_results(player, equipment, eq_name, search_type, bot_counter)
local radius = get_chip_radius(player, eq_name)
local area = Position.expand_to_area(player.position, radius)
local item_entities = equipment and bot_counter(0) > 0 and player.surface.find_entities_filtered {area = area, type = search_type, limit = 200}
local num_items = item_entities and #item_entities or 0
local num_chips = item_entities and #equipment or 0
return equipment, item_entities, num_items, num_chips, bot_counter
end
local function mark_items(player, item_equip, items, num_items, num_item_chips, bot_counter)
while num_items > 0 and num_item_chips > 0 and bot_counter(0) > 0 do
local item_chip = items and item_equip[num_item_chips]
while num_items > 0 and item_chip and item_chip.energy >= 50 do
local item = items[num_items]
if item and not item.to_be_deconstructed(player.force) then
item.order_deconstruction(player.force)
bot_counter(-1)
item_chip.energy = item_chip.energy - 50
end
num_items = num_items - 1
end
num_item_chips = num_item_chips - 1
end
end
--Mark items for deconstruction if player has roboport
local function process_ready_chips(player, equipment)
local rad = player.character.logistic_cell.construction_radius
local enemy = player.surface.find_nearest_enemy {position = player.position, max_distance = rad + 10, force = player.force}
if not enemy and (equipment['equipment-bot-chip-items'] or equipment['equipment-bot-chip-trees']) then
local bots_available = get_bot_counts(player.character)
if bots_available > 0 then
local bot_counter = function()
local count = bots_available
return function(add_count)
count = count + add_count
return count
end
end
bot_counter = bot_counter()
mark_items(player, get_chip_results(player, equipment['equipment-bot-chip-items'], 'equipment-bot-chip-items', 'item-entity', bot_counter))
mark_items(player, get_chip_results(player, equipment['equipment-bot-chip-trees'], 'equipment-bot-chip-trees', 'tree', bot_counter))
end
end
if enemy and equipment['equipment-bot-chip-launcher'] then
local launchers = equipment['equipment-bot-chip-launcher']
local num_launchers = #launchers
local capsule_data = get_best_follower_capsule(player)
if capsule_data then
local max_bots = player.force.maximum_following_robot_count + player.character_maximum_following_robot_count_bonus
local existing = #player.following_robots
local next_capsule = 1
local capsule = capsule_data[next_capsule]
while capsule and existing < (max_bots - capsule.qty) and capsule.count > 0 and num_launchers > 0 do
local launcher = launchers[num_launchers]
while capsule and existing < (max_bots - capsule.qty) and launcher and launcher.energy >= 500 do
if player.remove_item({name = capsule.capsule, count = 1}) == 1 then
player.surface.create_entity {name = capsule.unit, position = player.position, force = player.force, target = player.character}
launcher.energy = launcher.energy - 500
capsule.count = capsule.count - 1
existing = existing + capsule.qty
if capsule.count == 0 then
next_capsule = next_capsule + 1
capsule = capsule_data[next_capsule]
end
end
end
num_launchers = num_launchers - 1
end
end
end
end
local function emergency_heal_shield(player, feeders, energy_shields)
local num_feeders = #feeders
local pos = increment_position(player.position)
--Only run if we have less than max shield, Feeder max energy is 480
for _, shield in pairs(energy_shields.shields) do
while num_feeders > 0 do
local feeder = feeders[num_feeders]
while feeder and feeder.energy > 120 do
if shield.shield < shield.max_shield * .75 then
local last_health = shield.shield
local heal, locale = get_health_capsules(player)
shield.shield = shield.shield + (heal * 1.5)
local health_line = {'nanobots.health_line', ceil(abs(shield.shield - last_health)), locale}
player.surface.create_entity {name = 'flying-text', text = health_line, color = defines.color.green, position = pos()}
feeder.energy = feeder.energy - 120
else
break
end
end
num_feeders = num_feeders - 1
end
if num_feeders == 0 then
return
end
end
end
local function emergency_heal_player(player, feeders)
local num_feeders = #feeders
local pos = increment_position(player.character.position)
local max_health = player.character.prototype.max_health * .75
while num_feeders > 0 do
local feeder = feeders[num_feeders]
while feeder and feeder.energy >= 120 do
if player.character.health < max_health then
local last_health = player.character.health
local heal, locale = get_health_capsules(player)
player.character.health = last_health + heal
local health_line = {'nanobots.health_line', ceil(abs(player.character.health - last_health)), locale}
feeder.energy = feeder.energy - 120
player.surface.create_entity {name = 'flying-text', text = health_line, color = defines.color.green, position = pos()}
else
return
end
end
num_feeders = num_feeders - 1
end
end
--(( BOT CHIPS ))-------------------------------------------------------------
function armormods.prepare_chips(player)
if is_personal_roboport_ready(player.character) then
local _, _, charged, energy_shields = get_valid_equipment(player.character.grid)
if charged['equipment-bot-chip-launcher'] or charged['equipment-bot-chip-items'] or charged['equipment-bot-chip-trees'] then
process_ready_chips(player, charged)
end
if charged['equipment-bot-chip-feeder'] then
if #energy_shields.shields > 0 then
emergency_heal_shield(player, charged['equipment-bot-chip-feeder'], energy_shields)
elseif player.character.health < player.character.prototype.max_health * .75 then
emergency_heal_player(player, charged['equipment-bot-chip-feeder'])
end
end
end
end
return armormods