265 lines
12 KiB
Lua
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
|