local locations = require(mod_name .. ".lualib.locations") local colony_controller = {} local colony_controller_data = { fast_mode = false, colonies = {} } local centre_box = function(position,width) return { left_top = { x = position.x - (width/2), y = position.y - (width/2) }, right_bottom = { x = position.x + (width/2), y = position.y + (width/2) } } end local explosion = function(surface,position) local types = {'blood-explosion-small'} local random_type = types[math.random(1,#types)] surface.create_entity({ name=random_type, position=position, speed=10, source=position, target=position, }) end local decorate = function(surface,position) local types = {'lichen'} local random_type = types[math.random(1,#types)] surface.create_decoratives({ check_collision = false, decoratives = { { name=random_type, position=position, amount=1 } }}) end local breed_spawner = function(colony) local position = colony.surface.find_non_colliding_position_in_box("rocket-silo",colony.area,1,true) --TODO: change to bounding box when api is updated if position and colony.next_build == nil then local box = centre_box(position,3) colony.next_build = { entity_to_build = "biter-spawner", cost = 5*15, contributed = 0, position = position, damage_box = box, debug_box_id = rendering.draw_rectangle({ left_top = box.left_top, right_bottom = box.right_bottom, color = {r=1,b=0,g=0.5,a=1}, forces = {game.forces.player}, surface = colony.surface, }) } rendering.set_visible(colony.next_build.debug_box_id,false) end print("queued a spawner to be built") return colony.next_build end local breed_worm = function(colony) local position = colony.surface.find_non_colliding_position_in_box("biter-spawner",colony.area,1,true) --TODO:change to bounding box when api is updated if position and colony.next_build == nil then local box = centre_box(position,2) colony.next_build = { entity_to_build = "small-worm-turret", cost = 3*15, contributed = 0, position = position, damage_box = box, debug_box_id = rendering.draw_rectangle({ left_top = box.left_top, right_bottom = box.right_bottom, color = { r = 1, b = 0.5, g = 0, a = 1 }, forces = { game.forces.player }, surface = colony.surface, }) } rendering.set_visible(colony.next_build.debug_box_id,false) end print("queued a worm to be built") return colony.next_build end local plan_colony = function(colony) local spawners = colony.surface.find_entities_filtered({name='biter-spawner',force='enemy',area=colony.area}) local worms = colony.surface.find_entities_filtered({name='small-worm-turret',force='enemy',area=colony.area}) local biters = colony.surface.find_entities_filtered({name='small-biter',force='enemy',area=colony.area}) if #spawners == 0 and #biters == 0 then colony.next_update = game.ticks_played + 1600 colony.populated = false return false else colony.populated = true end if #spawners == 0 then breed_spawner(colony) colony.next_update = game.ticks_played + 60 + math.random(1,10) elseif #spawners < colony.max_spawners and #worms < colony.max_worms and math.random() < 0.5 then breed_worm(colony) colony.next_update = game.ticks_played + 300 + math.random(1,10) elseif #spawners < colony.max_spawners then breed_spawner(colony) colony.next_update = game.ticks_played + 300 + math.random(1,10) elseif #worms < colony.max_worms then breed_worm(colony) colony.next_update = game.ticks_played + 300 + math.random(1,10) else --nest complete colony.next_update = game.ticks_played + 3000 + math.random(1,10) return false end return true end local breed_colony = function(colony) global.DEBUG_MODE = false if colony.next_build and global.DEBUG_MODE then rendering.set_visible(colony.debug_box_id,true) else rendering.set_visible(colony.debug_box_id,false) end if colony.next_build == nil then return plan_colony(colony) end local biters = colony.surface.find_entities_filtered({name='small-biter',force='enemy',area=colony.area}) if #biters > 0 and colony.next_build then for _, biter in pairs(biters) do if math.random() < 0.2 then decorate(colony.surface,biter.position) end end local builders = colony.surface.find_entities_filtered({name='small-biter',force='enemy',area=colony.next_build.damage_box}) if #builders > 0 then --kill the biter and contribute its HP to the construction project colony.next_build.contributed = colony.next_build.contributed + builders[1].health decorate(colony.surface,builders[1].position) explosion(colony.surface,builders[1].position) builders[1].destroy() if colony.next_build.contributed >= colony.next_build.cost then colony.surface.create_entity({name=colony.next_build.entity_to_build,position=colony.next_build.position}) for dx = -5, 5 do for dy = -5, 5 do if math.random() < 0.3 then explosion(colony.surface,{x=colony.next_build.position.x+dx,y=colony.next_build.position.y+dy}) decorate(colony.surface,{x=colony.next_build.position.x+dx,y=colony.next_build.position.y+dy}) end end end print("a new "..colony.next_build.entity_to_build.." has been bred") rendering.destroy(colony.next_build.debug_box_id) colony.next_build = nil colony.next_update = game.ticks_played + 1200 + math.random(1,10) else print("biter sacrificed itself for the nest. Construction completion: ".. string.format("%02d/%02d",colony.next_build.contributed,colony.next_build.cost)) colony.next_update = game.ticks_played + 100 + math.random(1,10) end else --send a biter to the build area local random_builder = math.random(1,#biters) biters[random_builder].set_command({ type = defines.command.compound, structure_type = defines.compound_command.return_last, commands = { { type = defines.command.go_to_location, destination = colony.next_build.position, radius = 0.1, }, { type = defines.command.stop, } } }) print("sent a local biter to build zone") colony.next_update = game.ticks_played + 600 + math.random(1,10) end elseif #biters == 0 then --pick a random biter on the surface to come here local all_biters = colony.surface.find_entities_filtered({name='small-biter',limit=20}) print("sending completly random builder biter "..tostring(#all_biters)) if #all_biters > 1 then local random_biter = math.random(1,#all_biters) all_biters[random_biter].set_command({ type = defines.command.compound, structure_type = defines.compound_command.return_last, commands = { { type = defines.command.go_to_location, destination = colony.next_build.position, radius = 0.1, }, { type = defines.command.stop, } } }) end colony.next_update = game.ticks_played + 3000 + math.random(1,10) end end local colonize = function(data) local player = game.players[data.player_index] if data.parameter == nil then player.print("this command requires a parameter :area_name:") return false end local area_name = data.parameter local area = locations.get_area(area_name) if area then local spawners_per_nest = math.random(2,5) local worms_per_nest = math.random(1,10) player.print("Added new colony to area: "..area_name) colony_controller.register(area_name,area,spawners_per_nest,worms_per_nest) else player.print("no area called "..data.parameter) end end colony_controller.is_populated = function(name) return colony_controller_data.colonies[name] and colony_controller_data.colonies[name].populated end colony_controller.send_to_area = function(biter,area) --local area = locations.get_area(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), } biter.set_command({ type = defines.command.compound, structure_type = defines.compound_command.return_last, commands = { { type = defines.command.go_to_location, destination = random_position, distraction = defines.distraction.none, radius = 1, }, { type = defines.command.wander, radius = 5, }, } }) end colony_controller.register = function(name,max_spawners,max_worms) local area = locations.get_area(name) local surface = global.main_surface if area == nil then print("attempted to register colony with invalid area name "..name) return false end local spawners = surface.find_entities_filtered({name='biter-spawner',force='enemy',area=area}) if #spawners > 0 then print("attempted to register new colony in populated area") return false elseif colony_controller_data.colonies[name] then print("attempted to register new colony with existing colony name") return false end local new_colony = { name = name, surface = surface, area = area, next_update = 0, populated = false, max_spawners = max_spawners, max_worms = max_worms, next_build = nil, debug_box_id = rendering.draw_rectangle({ left_top = area.left_top, right_bottom = area.right_bottom, color = {r=1,b=1,g=1,a=1}, forces = {game.forces.player}, surface = surface, }) } colony_controller_data.colonies[name] = new_colony print("registered new colony successfully") rendering.set_visible(new_colony.debug_box_id,false) plan_colony(new_colony) return new_colony end colony_controller.deregister = function(name) if colony_controller_data.colonies[name] then rendering.destroy(colony_controller_data.colonies[name].debug_box_id) colony_controller_data.colonies[name] = nil return true end return false end colony_controller.update = function() local update_interval if colony_controller_data.fast_mode then update_interval = 20 else update_interval = 160 end if game.ticks_played % update_interval == 0 then for _, colony in pairs(colony_controller_data.colonies) do if colony_controller_data.fast_mode or colony.next_update < game.ticks_played then print("updating biter colony "..colony.name) breed_colony(colony) end end end end colony_controller.set_fast_mode = function(value) colony_controller_data.fast_mode = value end colony_controller.init = function() global.colony_controller_data = colony_controller_data end colony_controller.on_load = function() colony_controller_data = global.colony_controller_data commands.add_command("colonize","Test the colonization", colonize) end colony_controller.events = { [defines.events.on_tick] = colony_controller.update, } return colony_controller