local locations = require(mod_name .. ".lualib.locations") local command_callback = require(mod_name .. ".lualib.command_callback") local colony_controller = require(mod_name .. ".lualib.colony_controller") local story = require("story_2") local attacks = {} -- Functions which calculate modifiers during play attacks.modifiers= {} attacks.modifiers.get_ammo_effect = function() if game.players[1].controller_type == defines.controllers.character then local ammo = game.players[1].get_inventory(defines.inventory.player_ammo).get_contents() local count = ammo['firearm-magazine'] or 0 local capped = math.min(count,10) return 1/10*capped else return 0 end end attacks.modifiers.ten_minutes = function() local ten_minutes = 60*10 local passed = story.get_seconds_passed_since_current_node_started("main_story") local ratio = math.max(math.min(passed/ten_minutes,1),0.01) return ratio end attacks.modifiers.get_turrets_effect = function() -- return 0-1 -- 8 turrets returns 1 local quant = 10 local count = 0 local turrets = global.main_surface.find_entities_filtered({ name = 'gun-turret', force = 'player' }) for _, turret in pairs(turrets) do local bullets = turret.get_inventory(defines.inventory.item_main).get_item_count('firearm-magazine') if bullets > 0 then count = count + 1 end end return 1/quant*math.min(count,quant) end attacks.modifiers.get_turret_effect = function() -- return 0-1 -- 1 turrets returns 1 local turrets = global.main_surface.find_entities_filtered({ name = 'gun-turret', force = 'player' }) for _, turret in pairs(turrets) do local bullets = turret.get_inventory(defines.inventory.item_main).get_item_count('firearm-magazine') if bullets > 0 then return 1 end end return 0 end attacks.modifiers.get_final_research_progress = function() -- return 0-1 as percent of final research complete local force = game.forces['player'] local progress if force.current_research and force.current_research.name == 'demo-science-pack' then progress = force.research_progress else progress = force.get_saved_technology_progress('demo-science-pack') end progress = progress or 0 --print("technology progress "..progress) return progress end local function calculate_cost(attack) local variable_cost = attack.cost.maximum - attack.cost.minimum local modifier = 0 local weight_total = 0 for _, scale_function in pairs(attack.modifiers) do local value = math.min(attacks.modifiers[scale_function.name](),1) for _=1, scale_function.weight do modifier = modifier + value weight_total = weight_total + 1 end --print("modifier name "..scale_function.name.." total mod now "..modifier/weight_total) end modifier = modifier/weight_total modifier = 1-modifier --print("total modifier "..modifier) local actual_cost = attack.cost.minimum + (variable_cost*modifier) --print("cost: "..math.floor(actual_cost).." modifier "..modifier) return actual_cost end attacks.random_position_from_area = function(area_name) local area = locations.get_area(area_name) assert(area,"attempted to use invalid area for random_position "..area_name) local w = area.right_bottom.x - area.left_top.x local h = area.right_bottom.y - area.left_top.y local random_position = { x = area.left_top.x + (math.random()*w), y = area.left_top.y + (math.random()*h), } return random_position end attacks.spawn_colonization_wave = function(quant,spawn,rally,target,force) local surface = global.main_surface local actual_pos = surface.find_non_colliding_position('small-biter',spawn,20,2) if actual_pos then local group = surface.create_unit_group({position=spawn,force=force}) for _=1, quant do local biter = surface.create_entity{name = "small-biter", position= actual_pos, force = force} actual_pos = surface.find_non_colliding_position('small-biter',spawn,20,2) group.add_member(biter) end if global.active_attacks == nil then global.active_attacks = {} end --TODO: find a way to add this consistantly on level load global.active_attacks[group.group_number] = group command_callback.set_command(group, { type = defines.command.compound, structure_type = defines.compound_command.return_last, commands = { { type = defines.command.go_to_location, destination=rally, distraction = defines.distraction.none }, { type = defines.command.go_to_location, destination=target, distraction = defines.distraction.none }, { type = defines.command.wander, radius = 3, wander_in_group = false, distraction = defines.distraction.by_damage, --ticks_to_wait = 1600, }, } }, 'print_outcome') end end attacks.spawn_attack_wave = function(quant,spawn,rally,target,force) local surface = global.main_surface local actual_pos = surface.find_non_colliding_position('small-biter',spawn,20,2) if actual_pos then local group = surface.create_unit_group({position=spawn,force=force}) for _=1, quant do local biter = surface.create_entity{name = "small-biter", position= actual_pos, force = force} actual_pos = surface.find_non_colliding_position('small-biter',spawn,20,2) group.add_member(biter) end if global.active_attacks == nil then global.active_attacks = {} end global.active_attacks[group.group_number] = group group.set_command({ type = defines.command.compound, structure_type = defines.compound_command.return_last, commands = { { type = defines.command.go_to_location, destination=rally, distraction = defines.distraction.by_damage }, { type = defines.command.go_to_location, destination=target, --radius = 5, distraction = defines.distraction.by_anything }, { type = defines.command.wander, radius = 30, wander_in_group = false, distraction = defines.distraction.by_anything, --TODO: update to distraction by_really_anything when it is ready ticks_to_wait = 160, }, } }) end end attacks.check_spawn_wave = function(attack,force) if game.tick % 60 == 0 then if not attack then print ("Wave not found") return false end local current_cost = 0 if type(attack.cost) == "number" then current_cost = attack.cost elseif type(attack.cost) == 'table' then current_cost = calculate_cost(attack) end local wave_size = 0 if type(attack.wave) == "number" and (global.pollution_unspent > attack.wave*current_cost) then wave_size = attack.wave elseif type(attack.wave) == "table" then if global.pollution_unspent > (current_cost*attack.wave.maximum) then wave_size = math.random(attack.wave.minimum,attack.wave.maximum) end end if current_cost > 0 and wave_size > 0 then --send a wave local rand_attack_vector = math.random(1,#attack.vector) local vector = attack.vector[rand_attack_vector] if vector and vector.mission == 'attack' then local rand_spawn = math.random(1,#vector.spawn) local spawn = vector.spawn[rand_spawn] local rally = vector.rally[math.random(1,#vector.rally)] local target = vector.target[math.random(1,#vector.target)] if type(spawn) == 'string' then spawn = locations.get_pos(spawn) end if type(rally) == 'string' then rally = locations.get_pos(rally) end if type(target) == 'string' then target = locations.get_pos(target) end if spawn and rally and target then --make some biters! global.pollution_unspent = global.pollution_unspent - (current_cost*wave_size) attacks.spawn_attack_wave(wave_size,spawn,rally,target,force or 'enemy') --print("Spawned "..wave_size.." biters") end elseif vector and vector.mission == 'colonize' then local rand_spawn = math.random(1,#vector.spawn) local spawn = vector.spawn[rand_spawn] local rally = vector.rally[math.random(1,#vector.rally)] local target = vector.target[math.random(1,#vector.target)] if colony_controller.is_populated(target) then --the target already has biters, so we dont need to send any new ones --print("Cancelled attack wave as colony target has population already") return nil end if type(spawn) == 'string' then spawn = locations.get_pos(spawn) end if type(rally) == 'string' then rally = locations.get_pos(rally) end if type(target) == 'string' then target = attacks.random_position_from_area(target) end if spawn and rally and target then --make some biters! current_cost = current_cost / 3 global.pollution_unspent = global.pollution_unspent - (current_cost*wave_size) attacks.spawn_colonization_wave(wave_size,spawn,rally,target,force or 'enemy') --print("sent colonization effort") end end end --print("Wave "..wave_size.." Cost "..current_cost.." Saved "..global.pollution_unspent) return wave_size end return nil end attacks.init = function() global.active_attacks = {} end return attacks