1325 lines
48 KiB
Lua

util = require("util")
colors = require("colors")
require("utils")
require "cameras"
require "particles"
require "stdlib/area/chunk"
require "stdlib/area/area"
function ReadRunTimeSettings(event)
global.chances = global.chances or {} -- in priority order of danger
global.chances.volcano = {min_evo=settings.global["bm-volcano-min_evo"].value ,chance=settings.global["bm-volcano-chance"].value, max_evo=settings.global["bm-volcano-max_evo"].value}
global.chances.spidertron = {min_evo=settings.global["bm-spidertron-min_evo"].value , chance=settings.global["bm-spidertron-chance"].value}
global.chances.biterzilla = {min_evo=settings.global["bm-biterzilla-min_evo"].value , chance=settings.global["bm-biterzilla-chance"].value}
global.chances.worms = {min_evo=settings.global["bm-worms-min_evo"].value ,chance=settings.global["bm-worms-chance"].value}
global.chances.brutals = {min_evo=settings.global["bm-brutals-min_evo"].value , chance=settings.global["bm-brutals-chance"].value}
global.chances.soldiers = {min_evo=settings.global["bm-soldiers-min_evo"].value ,chance=settings.global["bm-soldiers-chance"].value}
global.chances.invasion = {min_evo=settings.global["bm-invasion-min_evo"].value , chance=settings.global["bm-invasion-chance"].value}
global.chances.swarm = {min_evo=0, chance=settings.global["bm-swarm-chance"].value, max_evo=settings.global["bm-swarm-max_evo"].value}
global.enable_silo_attack = settings.global["bm-enable-silo-attack"].value
global.show_cameras = settings.global["bm-show-cameras"].value
global.show_alerts = settings.global["bm-show-alerts"].value
global.play_sound_alert = settings.global["bm-play-sound-alert"].value
global.days = settings.global["bm-event-days"].value
global.allow_nuker = settings.global["bm-allow-nuker"].value
global.difficulty_level = settings.global["bm-difficulty-level"].value
global.spidertron_nuke = settings.global["bm-spidertron-nuke"].value
global.tree_events_chance = settings.global["bm-tree-events-chance"].value/100
global.spawn_near_nests = settings.global["bm-spawn_near_nests"].value
if event and event.setting_type=='runtime-per-user' then
local player = game.players[event.player_index]
if event.setting=='bm_draggable_camera' then
global.enable_drag_camera[player.name] = settings.get_player_settings(player)["ic_draggable_camera"].value
elseif event.setting=='bm_camera_size' then
global.camera_size[player.name] = settings.get_player_settings(player)["ic_camera_size"].value
end
end
end
script.on_event(defines.events.on_runtime_mod_setting_changed, ReadRunTimeSettings)
function setup_mod_vars()
global.player_forces = global.player_forces or {'player'}
global.surfaces = global.surfaces or {'nauvis'}
global.bm_volcano = global.bm_volcano or {}
global.biterzillas = global.biterzillas or {}
global.invasions = global.invasions or {}
global.rocket_silos = global.rocket_silos or {}
global.other_enemies = global.other_enemies or {}
global.next_wave = global.next_wave or {}
ReadRunTimeSettings()
global.next_event = global.next_event or (game.tick + (global.days * 7 * 60 * 60))
global.next_silo_event = global.next_silo_event or (game.tick + (global.days * 4 * 60 * 60))
end
function on_init()
cam_on_init()
setup_mod_vars()
RegisterModEvents()
end
script.on_init(on_init)
function on_configuration_changed()
cam_on_init()
setup_mod_vars()
end
script.on_configuration_changed(on_configuration_changed)
function on_load()
RegisterModEvents()
end
script.on_load(on_load)
-- camera events
local function on_gui_click(event)
cam_on_gui_click(event)
end
script.on_event(defines.events.on_gui_click, on_gui_click)
------------------------------------------------------------------------------------------
function pick_event(excludes)
local the_event
local evo = game.forces.enemy.evolution_factor
if not excludes then excludes={} end
for event, chances in pairs (global.chances) do
if not in_list(excludes,event) then
if evo >= chances.min_evo and math.random(100) <= chances.chance then
if (not chances.max_evo) or (chances.max_evo>=evo) then
the_event = event
break
end
end
end
end
return the_event
end
function Play_sound_alert(force,id)
if global.play_sound_alert then
local sound
if id==1 then
if game.active_mods['mferrari-mod-sounds'] then sound = 'mferrari_tense_music_1m' else sound='tc_sound_alarm_1' end
elseif id==2 then
if game.active_mods['mferrari-mod-sounds'] then sound = 'mferrari_tense_music_2m' else sound='tc_sound_alarm_2' end
elseif id==3 then
if game.active_mods['mferrari-mod-sounds'] then sound = 'mferrari_tense_music_3m' else sound='tc_sound_siren' end
end
if game.active_mods['mferrari-mod-sounds'] then force.play_sound{path=sound,override_sound_type='ambient' } else force.play_sound{path=sound} end
end
end
--the_event = biterzilla,soldiers,worms,volcano,invasion,swarm
function CreateNewEvent(the_event,surfacename, forcename)
local sufaces_tab = table.deepcopy(global.surfaces)
if surfacename then add_list(sufaces_tab, surfacename) end
local next_waves={}
for s=1,#sufaces_tab do
local surface = game.surfaces[sufaces_tab[s]]
if not the_event then the_event=pick_event() end
for p=1,#global.player_forces do
local pforce = game.forces[global.player_forces[p]]
if surface and the_event and pforce and (not surfacename or surface.name==surfacename) and (not forcename or pforce.name==forcename) then
--DRD
if (surface.peaceful_mode or surface.map_gen_settings.autoplace_controls["enemy-base"].size == 0) and not settings.global["bm-events-when-peaceful"].value then
break
end
--DRD
local player_spawn = pforce.get_spawn_position(surface)
local pcount = #pforce.connected_players
if pcount>0 then
local target
if the_event=='swarm' then
local last_building = FindTeamAttackCorner(surface, pforce, player_spawn,1)
local attack = {surface=surface,target=last_building} --(at.surface,at.target,at.limit,at.multiplier)
table.insert (next_waves,attack)
--CallFrenzyAttack(surface,last_building)
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-swarm"},colors.lightred)
end
Play_sound_alert(pforce,1)
elseif the_event=='invasion' then
local last_building, player_chunks = FindTeamAttackCorner(surface, pforce, player_spawn,1)
local rchunk = player_chunks[math.random(#player_chunks)]
local target = {x= rchunk.x*32+math.random(31), y= rchunk.y*32+math.random(31)}
create_invasion(surface,target,pcount)
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-invasion"},colors.lightred)
end
Play_sound_alert(pforce,1)
if global.show_cameras then CreateCameraForForce(pforce,target,surface,nil,nil,120,0.15) end
elseif the_event=='volcano' then
local last_building, player_chunks = FindTeamAttackCorner(surface, pforce, player_spawn,1)
local rchunk = player_chunks[math.random(#player_chunks)]
local target = {x= rchunk.x*32+math.random(31), y= rchunk.y*32+math.random(31)}
local volcano = create_volcano(surface, target, pcount)
if volcano then
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-volcano"},colors.lightred)
end
Play_sound_alert(pforce,2)
if global.show_cameras then CreateCameraForForce(pforce,volcano,surface,nil,nil,120,0.15) end
end
elseif the_event=='worms' then
local last_building, player_chunks = FindTeamAttackCorner(surface, pforce, player_spawn,1)
local rchunk = player_chunks[math.random(#player_chunks)]
local target = {x= rchunk.x*32+math.random(31), y= rchunk.y*32+math.random(31)}
local bigworm = create_bigworm(surface,target,pcount)
if bigworm then
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-worm"},colors.lightred)
end
Play_sound_alert(pforce,2)
if global.show_cameras then CreateCameraForForce(pforce,bigworm,surface,nil,nil,120,0.15) end
end
elseif the_event=='soldiers' then
local attack1, player_chunks, spawn = FindTeamAttackCorner(surface, pforce, player_spawn,4)
local rchunk = player_chunks[math.random(#player_chunks)]
local attack2 = {x= rchunk.x*32+math.random(31), y= rchunk.y*32+math.random(31)}
if spawn then
if global.spawn_near_nests then spawn = get_pos_near_enemy_nest(surface,spawn,pforce) end
local group, humie = create_soldiers_group(surface,spawn,pcount,attack1, attack2, player_spawn)
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-human_soldiers"},colors.lightred)
end
Play_sound_alert(pforce,2)
if global.show_cameras then CreateCameraForForce(pforce,humie,surface,nil,nil,120,0.15) end
end
elseif the_event=='brutals' then
local attack1, player_chunks, spawn = FindTeamAttackCorner(surface, pforce, player_spawn,4)
local rchunk = player_chunks[math.random(#player_chunks)]
local attack2 = {x= rchunk.x*32+math.random(31), y= rchunk.y*32+math.random(31)}
if spawn then
if global.spawn_near_nests then spawn = get_pos_near_enemy_nest(surface,spawn,pforce) end
local group, brutal = create_brutals_group(surface,spawn,pcount,attack1, attack2, player_spawn)
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-brutals"},colors.lightred)
end
Play_sound_alert(pforce,2)
if global.show_cameras then CreateCameraForForce(pforce,brutal,surface,nil,nil,120,0.15) end
end
elseif the_event=='biterzilla' then
local attack1, player_chunks, spawn = FindTeamAttackCorner(surface, pforce, player_spawn,4)
local rchunk = player_chunks[math.random(#player_chunks)]
local attack2 = {x= rchunk.x*32+math.random(31), y= rchunk.y*32+math.random(31)}
if spawn then
if global.spawn_near_nests then spawn = get_pos_near_enemy_nest(surface,spawn,pforce) end
local group, zilla = create_biterzilla(surface,spawn,pcount,attack1, attack2, player_spawn)
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-biterzilla"},colors.lightred)
end
Play_sound_alert(pforce,3)
if global.show_cameras then CreateCameraForForce(pforce,zilla,surface,nil,nil,120,0.15) end
end
elseif the_event=='spidertron' then
local attack1, player_chunks, spawn = FindTeamAttackCorner(surface, pforce, player_spawn,4)
local rchunk = player_chunks[math.random(#player_chunks)]
local attack2 = {x= rchunk.x*32+math.random(31), y= rchunk.y*32+math.random(31)}
if spawn then
if global.spawn_near_nests then spawn = get_pos_near_enemy_nest(surface,spawn,pforce) end
local spider = create_spidertron(surface,spawn,pcount)
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-biterzilla"},colors.lightred)
end
Play_sound_alert(pforce,3)
if global.show_cameras then CreateCameraForForce(pforce,spider,surface,nil,nil,120,0.15) end
end
elseif the_event=='ultimate_boss' then
local attack1, player_chunks, spawn = FindTeamAttackCorner(surface, pforce, player_spawn,10)
local rchunk = player_chunks[math.random(#player_chunks)]
local attack2 = {x= rchunk.x*32+math.random(31), y= rchunk.y*32+math.random(31)}
if spawn then
if global.spawn_near_nests then spawn = get_pos_near_enemy_nest(surface,spawn,pforce) end
local group, zilla = create_biterzilla(surface,spawn,pcount,attack1, attack2, player_spawn,'tc_fake_human_ultimate_boss_cannon_20')
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-biterzilla"},colors.lightred)
end
Play_sound_alert(pforce,3)
if global.show_cameras then CreateCameraForForce(pforce,zilla,surface,nil,nil,120,0.15) end
end
end
end --pcount>0
end --the_event
end -- p
end
if #next_waves>0 then
local waves = 2 + math.ceil(global.difficulty_level) + math.floor (game.forces.enemy.evolution_factor * 6)
for w=1,waves do
local tick = game.tick + ((w-1)*60*40 )
local new_wave = {tick=tick, event_name=the_event, attacks=next_waves}
table.insert(global.next_wave,new_wave)
end
end
end
function get_pos_near_enemy_nest(surface,spawn,pforce)
local enemy = surface.find_nearest_enemy{position=spawn, max_distance=3000, force=pforce}
if enemy then
local f = enemy.force
local nests = surface.find_entities_filtered{type='unit-spawner', position=enemy.position, radius=300, force=f}
if #nests>0 then
spawn = get_random_pos_near(nests[math.random(#nests)].position,30)
spawn = surface.find_non_colliding_position('assembling-machine-1', spawn, 0, 1)
end
end
return spawn
end
function Create_Position_Event(the_event, surface, position, pforce)
--DRD
if (surface.peaceful_mode or surface.map_gen_settings.autoplace_controls["enemy-base"].size == 0) and not settings.global["bm-events-when-peaceful"].value then
return
end
--DRD
local pcount = #pforce.connected_players
if the_event=='swarm' then
CallFrenzyAttack(surface,position)
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-swarm"},colors.lightred)
end
elseif the_event=='invasion' then
local target = get_random_pos_near(position,150)
target = surface.find_non_colliding_position('rocket-silo', target, 60, 1)
if target then
create_invasion(surface,target,pcount)
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-invasion"},colors.lightred)
end
Play_sound_alert(pforce,1)
if global.show_cameras then CreateCameraForForce(pforce,target,surface,nil,nil,120,0.15) end
end
elseif the_event=='volcano' then
local target = get_random_pos_near(position,150)
target = surface.find_non_colliding_position('rocket-silo', target, 50, 1)
if target then
local volcano = create_volcano(surface, target, pcount)
if volcano then
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-volcano"},colors.lightred)
end
Play_sound_alert(pforce,2)
if global.show_cameras then CreateCameraForForce(pforce,volcano,surface,nil,nil,120,0.15) end
end
end
elseif the_event=='worms' then
local target = get_random_pos_near(position,150)
target = surface.find_non_colliding_position('rocket-silo', target, 50, 1)
if target then
local bigworm = create_bigworm(surface,target,pcount)
if bigworm then
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-worm"},colors.lightred)
end
Play_sound_alert(pforce,2)
if global.show_cameras then CreateCameraForForce(pforce,bigworm,surface,nil,nil,120,0.15) end
end
end
elseif the_event=='soldiers' then
local spawn = surface.find_nearest_enemy{position=position, max_distance=500, force=pforce}
if spawn then
spawn = get_random_pos_near(spawn.position,30)
local group, humie = create_soldiers_group(surface,spawn,pcount,position)
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-human_soldiers"},colors.lightred)
end
Play_sound_alert(pforce,2)
if global.show_cameras then CreateCameraForForce(pforce,humie,surface,nil,nil,120,0.15) end
end
elseif the_event=='brutals' then
local spawn = surface.find_nearest_enemy{position=position, max_distance=500, force=pforce}
if spawn then
spawn = get_random_pos_near(spawn.position,30)
local group, brutal = create_brutals_group(surface,spawn,pcount,position)
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-brutals"},colors.lightred)
end
Play_sound_alert(pforce,2)
if global.show_cameras then CreateCameraForForce(pforce,brutal,surface,nil,nil,120,0.15) end
end
elseif the_event=='biterzilla' then
local spawn = surface.find_nearest_enemy{position=position, max_distance=500, force=pforce}
if spawn then
spawn = get_random_pos_near(spawn.position,30)
local group, zilla = create_biterzilla(surface,spawn,pcount,position)
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-biterzilla"},colors.lightred)
end
Play_sound_alert(pforce,3)
if global.show_cameras then CreateCameraForForce(pforce,zilla,surface,nil,nil,120,0.15) end
end
end
end
function Create_Silo_Attack(the_event)
if not the_event then the_event=pick_event() end
if the_event then
for s=#global.rocket_silos,1,-1 do
local silo=global.rocket_silos[s]
if not (silo and silo.valid) then table.remove(global.rocket_silos,s)
else
local pforce = silo.force
local pcount = #pforce.connected_players
local surface = silo.surface
if pcount>0 then Create_Position_Event(the_event, surface, silo.position, pforce) end
end
end
end
end
script.on_nth_tick(60*60, function (event)
if global.days>0 then
if global.next_event<game.tick then
CreateNewEvent()
global.next_event = game.tick + (global.days * (7 + math.random(-2,2)) * 60 * 60)
end
if global.next_silo_event < game.tick and global.enable_silo_attack then
Create_Silo_Attack()
global.next_silo_event = game.tick + (global.days * (7 + math.random(-4,-2)) * 60 * 60)
end
end
end)
function FindTeamAttackCorner(surface, force, initial_position, chunk_distance)
local directionVec=GetRandomVector()
if not chunk_distance then chunk_distance=4 end
local enemy_spawn
local nothing = 0
local chunkPos = Chunk.from_position(initial_position) --{x=0,y=0} initial_position
local player_chunks = {chunkPos}
local last_building = chunkPos
-- Keep checking chunks in the direction of the vector
while(not enemy_spawn) do
chunkPos.x = chunkPos.x + directionVec.x
chunkPos.y = chunkPos.y + directionVec.y
-- Set some absolute limits.
if ((math.abs(chunkPos.x) > 2000) or (math.abs(chunkPos.y) > 2000)) then break
elseif (surface.is_chunk_generated(chunkPos)) then
local area = Chunk.to_area(chunkPos)
local builds = surface.find_entities_filtered{force=force,area=area,limit=1}
if #builds>0 then
last_building = table.deepcopy(chunkPos)
table.insert (player_chunks,table.deepcopy(chunkPos))
else
nothing = nothing + 1
if nothing >= chunk_distance then enemy_spawn = chunkPos end
end
-- Found an ungenerated area
else break end
end
last_building = {x= last_building.x*32+math.random(31), y= last_building.y*32+math.random(31)}
if enemy_spawn then
enemy_spawn = {x= enemy_spawn.x*32+math.random(31), y= enemy_spawn.y*32+math.random(31)}
enemy_spawn = surface.find_non_colliding_position('rocket-silo', enemy_spawn, 100, 1)
end
return last_building, player_chunks, enemy_spawn
end
function group_set_command(group,position_1,position_2, position_3)
local def = defines
local compound = def.command.compound
local structure = def.compound_command.return_last
local go_to = def.command.attack_area
if not position_2 then position_2=position_1 end
if not position_3 then position_3=position_2 end
group.set_command(
{
type = compound,
structure_type = structure,
commands =
{
{type = go_to, destination = position_1, distraction = defines.distraction.by_anything, radius=50},
{type = go_to, destination = position_2, distraction = defines.distraction.by_enemy, radius=30},
{type = go_to, destination = position_3, distraction = defines.distraction.by_enemy, radius=30},
{type = defines.command.wander, distraction = defines.distraction.by_enemy, radius=30},
}
})
--group.start_moving()
end
--------------------------------------------------------------------------------------
function create_soldiers_group(surface,spawn,pcount, attack1, attack2,attack3)
local qt = math.min(10, math.max(2,math.ceil(game.forces.enemy.evolution_factor * 10 + math.random(math.ceil(pcount/2))))) + (global.difficulty_level-1) * 12
local group
local one_humie
for x=1, qt do
local humie = get_random_human_soldier()
local position = surface.find_non_colliding_position(humie, spawn, 100, 1)
if position then
if not group then group = surface.create_unit_group{position = position, force = game.forces.enemy} end
one_humie = surface.create_entity{name=humie, position= position, force= game.forces.enemy}
if one_humie and one_humie.valid then group.add_member(one_humie) table.insert (global.other_enemies,one_humie ) end
end
end
if game.forces.enemy.evolution_factor>0.91 and math.random(2)==1 then
local humie = get_random_boss_human_soldier(0)
local position = surface.find_non_colliding_position(humie, spawn, 100, 1)
if not group then group = surface.create_unit_group{position = position, force = game.forces.enemy} end
one_humie = surface.create_entity{name=humie, position=position, force = game.forces.enemy}
if one_humie and one_humie.valid then group.add_member(one_humie) table.insert(global.other_enemies,one_humie) end
end
group_set_command(group,attack1,attack2,attack3)
return group, one_humie
end
function create_invasion(surface,position,pcount)
local qt = math.min(25, math.max(1,math.ceil(game.forces.enemy.evolution_factor * 20 + math.random(math.ceil(pcount/2))))) + global.difficulty_level - 1
table.insert (global.invasions, {name="bm-spawner", surface=surface, position=position, quant=qt})
if game.forces.enemy.evolution_factor>0.8 then
if math.random(1,2)==1 then CallWormAttack(surface,position,math.ceil((qt+ global.difficulty_level )/2),0,25) end
end
end
function get_random_human_soldier(evolution)
if not evolution then evolution = game.forces.enemy.evolution_factor end
local list = { 'tc_fake_human_machine_gunner',
'tc_fake_human_laser',
'tc_fake_human_electric'}
if evolution>0.17 then
table.insert(list,'tc_fake_human_sniper')
else
table.insert(list,'tc_fake_human_melee')
table.insert(list,'tc_fake_human_pistol_gunner')
end
if evolution>0.3 then table.insert(list,'tc_fake_human_grenade') end
if evolution>0.60 then table.insert(list,'tc_fake_human_rocket') table.insert(list,'tc_fake_human_cannon') end
if evolution>0.75 then table.insert(list,'tc_fake_human_erocket') table.insert(list,'tc_fake_human_cluster_grenade') table.insert(list,'tc_fake_human_cannon_explosive') end
if global.allow_nuker and evolution>0.96 then table.insert(list,'tc_fake_human_nuke_rocket') end
local n=math.min(10, math.max(math.ceil(evolution*10),1))
return list[math.random(#list)] ..'_'..n
end
function get_random_boss_human_soldier(evolution)
if not evolution then evolution = game.forces.enemy.evolution_factor end
local list = { 'tc_fake_human_boss_machine_gunner',
'tc_fake_human_boss_laser',
'tc_fake_human_boss_electric'}
if evolution>0.94 then table.insert(list,'tc_fake_human_boss_sniper') end
if evolution>0.96 then
table.insert(list,'tc_fake_human_boss_rocket')
table.insert(list,'tc_fake_human_boss_grenade')
end
if evolution>0.98 then
table.insert(list,'tc_fake_human_boss_erocket')
table.insert(list,'tc_fake_human_boss_cluster_grenade')
table.insert(list,'tc_fake_human_boss_cannon_explosive')
end
local n=math.min(10, math.max(math.ceil((evolution - 0.9) *100),1))
return list[math.random(#list)] ..'_'..n
end
function get_brutals_for_evolution(evolution,extra_evo)
local brutals = {}
local biters = get_units_for_evolution(evolution,extra_evo)
for b=1,#biters do
local brut = 'brutal-'..biters[b]
if game.entity_prototypes[brut] then
table.insert(brutals,brut)
end
end
return brutals
end
function create_brutals_group(surface,spawn,pcount, attack1, attack2,attack3)
local qt = math.min(10, math.max(2,math.ceil(game.forces.enemy.evolution_factor * 10 + math.random(math.ceil(pcount/2))))) + (global.difficulty_level-1) * 10
local group
local one_brutal
local brutals = get_brutals_for_evolution(nil,0.1)
for x=1, qt do
local brutal = brutals[math.random(#brutals)]
local position = surface.find_non_colliding_position(brutal, spawn, 200, 1)
if position then
if not group then
group = surface.create_unit_group{position = position, force = game.forces.enemy}
surface.create_entity{name="bm-large-tunnel", position=position, force = game.forces.neutral}
create_remnants_particles (surface, math.random(30,50) , position)
end
one_brutal = surface.create_entity{name=brutal, position= position, force= game.forces.enemy}
create_tatoo_for_unit(one_brutal)
group.add_member(one_brutal)
table.insert (global.other_enemies,one_brutal )
end
end
group_set_command(group,attack1,attack2,attack3)
return group, one_brutal
end
function get_random_lesser_boss(evolution,surface)
if not evolution then evolution = game.forces.enemy.evolution_factor end
local list = {'maf-boss-biter-','maf-boss-acid-spitter-'}
if game.active_mods['ArmouredBiters'] then table.insert(list,'maf-boss-armoured-biter-') end
if game.active_mods['Cold_biters'] then
if (not surface) or #surface.find_entities_filtered{name='cb-cold-spawner',limit=1}>0 then
table.insert(list,'maf-boss-frost-biter-')
table.insert(list,'maf-boss-frost-spitter-')
end end
if game.active_mods['Explosive_biters'] then
if (not surface) or #surface.find_entities_filtered{name='explosive-biter-spawner',limit=1}>0 then
table.insert(list,'maf-boss-explosive-biter-')
table.insert(list,'maf-boss-explosive-spitter-')
end end
if game.active_mods['Toxic_biters'] then
if (not surface) or #surface.find_entities_filtered{name='toxic-biter-spawner',limit=1}>0 then
table.insert(list,'maf-boss-toxic-biter-')
table.insert(list,'maf-boss-toxic-spitter-')
end end
if game.active_mods['Arachnids_enemy'] then
if (not surface) or #surface.find_entities_filtered{name='arachnid-spawner-unitspawner',limit=1}>0 then
table.insert(list,'maf-boss-arachnid-biter-')
end end
local n=math.min(10, math.max(math.ceil(evolution*10),1))
return list[math.random(#list)] ..n
end
function Call_next_wave(event)
local event_name = event.event_name
if event_name == 'swarm' then
local attacks = event.attacks
for a=1,#attacks do
local at = attacks[a]
CallFrenzyAttack(at.surface,at.target,at.limit,at.multiplier)
end
end
end
function CallFrenzyAttack(surface,target,limit,multiplier)
if not (surface and surface.valid) then return end
if surface.map_gen_settings.autoplace_controls and surface.map_gen_settings.autoplace_controls["enemy-base"]
and surface.map_gen_settings.autoplace_controls["enemy-base"].size>0 then
local position
if target.type and target.valid
then
position=target.position
else
position = target
end
local Min = global.difficulty_level * 10
local Max = 270 + global.difficulty_level * 30
local Dist = 1500
local spawn = surface.find_entities_filtered({type = "unit-spawner",limit=1,position=position,radius=Dist})
if #spawn>0 then
local force = spawn[1].force
local aliens = Max * force.evolution_factor
if aliens < Min then aliens=Min end
if limit and aliens>limit then aliens=limit end
if multiplier then aliens=math.floor(aliens * multiplier) end
if target.type and target.valid then
local sent = surface.set_multi_command({
command = {type=defines.command.attack, target=target, distraction = defines.distraction.by_anything },
unit_count = aliens,
force = force,
unit_search_distance = Dist,
})
else
local sent = surface.set_multi_command({
command = {type=defines.command.attack_area, destination=position, radius=50, distraction = defines.distraction.by_anything },
unit_count = aliens,
force = force,
unit_search_distance = Dist,
})
end
end
end
end
function CallWormAttack(surface,position,how_many,min_distance,max_distance,force)
local worms = get_worms_for_evolution(nil,nil,surface,position)
for w=1,how_many do
local worm = worms[math.random(#worms)]
for t=1,10 do
local x = position.x + math.random(min_distance,max_distance)* RandomNegPos()
local y = position.y + math.random(min_distance,max_distance)* RandomNegPos()
local pos = surface.find_non_colliding_position(worm, {x=x,y=y}, 6, 2)
if pos then
create_remnants_particles (surface, math.random(20,40) , pos)
local wo = surface.create_entity{name=worm,force=force,position=pos}
table.insert (global.other_enemies,wo )
break
end
end
end
end
--------------------------------------------------------
function create_biterzilla(surface,spawn,pcount, attack1, attack2, attack3,specific_zilla)
local zilla, name, group
local evo = game.forces.enemy.evolution_factor
if evo<0.9 and (not specific_event) then
name = get_random_lesser_boss(evo,surface)
else
local list = {"biterzilla1","biterzilla2","biterzilla3","maf-giant-acid-spitter","maf-giant-fire-spitter","bm-motherbiterzilla"}
if global.chances.soldiers.chance>0 then table.insert(list,"big_human") end
name = list[math.random(#list)]
if name=="big_human" then name=get_random_boss_human_soldier()
elseif string.find(name, "zilla") or string.find(name, "maf") then
local N=1
if evo>0.98 then N=5
elseif evo>0.96 then N=4
elseif evo>0.94 then N=3
elseif evo>0.92 then N=2
end
name=name..N
end
end
if specific_zilla then name=specific_zilla end
local position = surface.find_non_colliding_position(name, spawn, 0, 1)
if position then
group = surface.create_unit_group{position = position, force = game.forces.enemy}
create_remnants_particles (surface, math.random(60,100) , position)
zilla = surface.create_entity{name=name, position=position, force = game.forces.enemy}
zilla.ai_settings.allow_destroy_when_commands_fail=false
surface.create_entity{name="bm-large-tunnel", position=position, force = game.forces.neutral}
group.add_member(zilla)
local effect
if specific_zilla then effect='all_buff'
elseif (string.find(name, "human") and evo>0.6 and math.random (3)==1) then effect='defender_capsules'
elseif (string.find(name, "human") and evo>0.85 and math.random (3)==1) then effect='grenade_launcher' end
table.insert (global.biterzillas, {entity=zilla, effect = effect})
if evo>=0.9 then
local hpe = (pcount*zilla.health/10) * game.forces.enemy.evolution_factor
if game.forces.enemy.evolution_factor>0.95 then hpe = hpe*1.5 end
zilla.health = (zilla.health/5) + hpe
end
if pcount>1 and game.forces.enemy.evolution_factor>0.95 then
if math.random(1,2)==1 then
CallFrenzyAttack(surface,attack1)
else
if global.chances.invasion.chance>=math.random(100) then create_invasion(surface,attack1,pcount) end
end
end
end
group_set_command(group,attack1,attack2, attack3)
return group, zilla
end
function create_spidertron(surface,spawn,pcount)
local spider
local evo = game.forces.enemy.evolution_factor
local N=1
if evo>0.98 then N=5
elseif evo>0.96 then N=4
elseif evo>0.94 then N=3
elseif evo>0.92 then N=2
end
local name='bm-spidertron_' .. N
local position = surface.find_non_colliding_position(name, spawn, 0, 1)
if position then
create_remnants_particles (surface, math.random(60,100) , position)
spider = surface.create_entity{name=name, position=position, force = game.forces.enemy}
surface.create_entity{name="bm-large-tunnel", position=position, force = game.forces.neutral}
local effect
if evo>0.6 and math.random (3)==1 then effect='defender_capsules' end
if evo>0.85 and math.random (3)==1 then effect='grenade_launcher' end
table.insert (global.biterzillas, {entity=spider, effect = effect})
if evo>=0.9 then
local hpe = (pcount*spider.health/10) * game.forces.enemy.evolution_factor
if game.forces.enemy.evolution_factor>0.95 then hpe = hpe*1.5 end
spider.health = ((spider.health/5) + hpe) * global.difficulty_level
end
end
return spider
end
function Spidertron_Moves(spider)
if global.spidertron_nuke then
spider.insert{name='maf-small-atomic-rocket',count=5}
end
local FI = spider.get_fuel_inventory()
if FI then
if FI.get_item_count('solid-fuel')<20 and FI.can_insert{name='solid-fuel', count=20} then spider.insert {name='solid-fuel', count=20} end
end
if game.forces.enemy.evolution_factor>0.95 then spider.insert{name='explosive-rocket',count=400} else spider.insert{name='rocket',count=400} end
local enemy = spider.surface.find_nearest_enemy{position=spider.position, max_distance=1500, force=spider.force} --zilla.force
if enemy and enemy.valid then
spider.autopilot_destination = get_random_pos_near(enemy.position,40)
end
end
local tick_zillas = 60
script.on_nth_tick(tick_zillas, function (event)
cam_on_tick(event)
for i=#global.invasions,1,-1 do
local inv =global.invasions[i]
local surface = inv.surface
if inv.quant>0 and surface.valid then
local position = inv.surface.find_non_colliding_position(inv.name, inv.position, 200, 4)
if position ~= nil then
create_remnants_particles (surface, math.random(20,40) , position)
local invader = surface.create_entity{name=inv.name, position=position, force = game.forces.enemy}
table.insert(global.other_enemies, invader)
end
inv.quant=inv.quant-1
else inv.quant=0
end
if inv.quant<1 then table.remove(global.invasions,i) end
end
-- ZILLAS
for z=#global.biterzillas,1,-1 do
local ZI = global.biterzillas[z]
local zilla = ZI.entity
local effect = ZI.effect
if not (zilla and zilla.valid) then table.remove(global.biterzillas,z)
else
if string.find(zilla.name, "biterzilla1") then
if math.random(1,4)==1 then
local xx = zilla.position.x + math.random(-5,5)
local yy = zilla.position.y + math.random(-5,5)
zilla.surface.create_entity{name ="maf-cluster-fire-projectile", target_position={x=xx,y=yy}, position={x=xx,y=yy}, source=zilla, force=zilla.force, speed=1}
end
elseif string.find(zilla.name, "biterzilla2") then
if math.random(3)==1 then zilla.surface.create_entity{name="electric-shock2", position=zilla.position, force=zilla.force} end
elseif string.find(zilla.name, "motherbiterzilla") then if math.random(1,4)==1 then Brood(zilla) end
elseif string.find(zilla.name, "human") and math.random(1,40)==1 then BroodHumans(zilla)
end
if string.find(zilla.name, "tc_fake_human_ultimate_boss") then
if math.random(1,10)==1 then BroodHumans(zilla, 2) end end
if effect and (effect=='defender_capsules' or effect=='all_buff') then
local ent = {'destroyer','defender'}
if math.random(3)==1 then zilla.surface.create_entity{name=ent[math.random(#ent)], position=zilla.position,target=zilla,force=zilla.force} end
end
if effect and (effect=='grenade_launcher' or effect=='all_buff') and math.random(5)==1 then
local d=35
if effect=='all_buff' then d=60 end
local enemy = zilla.surface.find_nearest_enemy{position=zilla.position, max_distance=d, force=zilla.force} --zilla.force
if enemy and enemy.valid and enemy.name~='entity-ghost' and enemy.type~='entity-ghost' and enemy.destructible and enemy.health then
local weap = {'explosive-rocket','grenade'}
if string.find(zilla.name, "ultimate") then table.insert(weap,'bm-small-atomic-rocket') table.insert(weap,'cluster-grenade') end
zilla.surface.create_entity{name=weap[math.random(#weap)], target=enemy, position=zilla.position, force=zilla.force, speed=0.4}
end
end
if zilla.valid and zilla.type == 'spider-vehicle' and game.tick % (tick_zillas*18) ==0 then Spidertron_Moves(zilla) end
end
end
end)
script.on_nth_tick(60*3, function (event)
if #global.next_wave>0 and global.next_wave[1].tick < game.tick then
Call_next_wave(global.next_wave[1])
table.remove(global.next_wave,1)
end
for k=#global.bm_volcano,1,-1 do
local V=global.bm_volcano[k]
local volcano = V.volcano
local tick = V.tiltick
if volcano and volcano.valid then
local pos = volcano.position
volcano.surface.pollute(pos,75)
volcano.surface.create_trivial_smoke{name='fire-smoke-on-adding-fuel', position=pos}
volcano.surface.create_trivial_smoke{name='turbine-smoke', position=pos}
if game.tick > tick then
volcano.die()
table.remove(global.bm_volcano,k)
else -- erupt!!!
volcano.surface.create_entity{name="big-artillery-explosion", position=pos, force = game.forces.neutral}
local area = 50
local xx = pos.x + math.random(-area,area)
local yy = pos.y + math.random(-area,area)
volcano.surface.create_entity{name ="maf-cluster-fire-projectile", target_position={x=xx,y=yy}, position={x=xx,y=yy}, source=volcano, force= game.forces.enemy, speed=4}
end
else
table.remove(global.bm_volcano,k)
end
end
end)
function create_volcano(surface, target, pcount)
local duration = (60 * 60) + ((pcount + global.difficulty_level) * 60 * 20)
local volcano
local position = surface.find_non_colliding_position("bm-volcano", target, 200, 1)
if position ~= nil then
surface.create_entity{name="nuke-explosion", position=position, force = game.forces.enemy} --big-artillery-explosion
volcano = surface.create_entity{name="bm-volcano", position=position, force = game.forces.enemy}
table.insert(global.bm_volcano, {volcano=volcano, tiltick=game.tick + duration})
end
return volcano
end
--------------------------------------------------------------------------
function create_bigworm(surface,target,pcount)
local worm = "maf-behemoth-worm-turret"
if game.forces.enemy.evolution_factor > 0.80 then worm = "maf-colossal-worm-turret" end
if game.forces.enemy.evolution_factor > 0.9 then
if math.random(1,2)==1 then worm = "bm-worm-boss-fire-shooter" else worm = "bm-worm-boss-acid-shooter" end
end
local worm = "maf-behemoth-worm-turret"
local qt = math.min(4, 1 + math.floor(pcount/4)) + global.difficulty_level - 1
local the_worm
for x=1,qt do
local position = surface.find_non_colliding_position(worm, target, 150, 1)
if position then
create_remnants_particles (surface, math.random(30,60) , position)
the_worm = surface.create_entity{name=worm, position= position, force= game.forces.enemy}
table.insert (global.other_enemies,the_worm )
end
end
-- normal worms for reinforced attack
if pcount>2 then
local qt = math.min(25, math.max(1,math.ceil(game.forces.enemy.evolution_factor * 12 + math.random(math.ceil(pcount/2))))) + global.difficulty_level - 1
CallWormAttack(surface,target,qt,0,20)
end
return the_worm
end
function Brood(mother)
local x = mother.position.x
local y = mother.position.y
local r = 10
local Units = mother.surface.find_entities_filtered({force=mother.force, area={{x-r,y-r},{x+r,y+r}}})
if #Units>50 then return end
local Min = 2
local Max = 6
local num = math.random(Min,Max) + global.difficulty_level - 1
local group = mother.unit_group
local children = get_units_for_evolution()
for k=1,num do
local name= children[math.random(#children)]
local pos = mother.surface.find_non_colliding_position(name, mother.position, 10, 1)
if pos then
mother.surface.create_entity{name="bm-acid-splash-fire", position=pos, force = mother.force}
local child = mother.surface.create_entity{name=name, position=pos, force = mother.force}
if group then group.add_member(child) end
end
end
end
function BroodHumans(mother, extra)
extra=extra or 0
local x = mother.position.x
local y = mother.position.y
local r = 10
local Units = mother.surface.find_entities_filtered({force= mother.force, area={{x-r,y-r},{x+r,y+r}}})
if #Units>5 + global.difficulty_level + extra*3 then return end
local num = math.random(3+ extra) + global.difficulty_level - 1
local group = mother.unit_group
for k=1,num do
local name= get_random_human_soldier()
local pos = mother.surface.find_non_colliding_position(name, mother.position, 15, 1)
if pos then
local child = mother.surface.create_entity{name=name, position=pos, force = mother.force}
if group then group.add_member(child) end
end
end
end
--------------------------------------------------------------------------------------
local function create_rocks(entity,quant)
local rocks = {'rock-big','rock-huge','sand-rock-big'}
entity.surface.create_entity{name='rock-huge', position=entity.position, force = game.forces.neutral}
for q=1,quant do
local rock = rocks[math.random(#rocks)]
local position = entity.surface.find_non_colliding_position(rock, entity.position, 10, 1)
if position ~= nil then
entity.surface.create_entity{name="big-artillery-explosion", position=position, force = game.forces.neutral}
entity.surface.create_entity{name=rock, position=position, force = game.forces.neutral}
end
end
end
function On_Remove(event)
if event.entity and event.entity.name == "bm-volcano" then
create_rocks(event.entity,12)
end
end
local filters = {{filter = "name", name = "bm-volcano"}}
script.on_event(defines.events.on_entity_died, On_Remove)
function On_Built(event)
local entity = event.created_entity or event.entity
if entity and entity.valid and entity.type~='entity-ghost' then
local surface = entity.surface
local name=entity.name
table.insert (global.rocket_silos,entity)
if global.enable_silo_attack then CallFrenzyAttack(surface,entity,nil,1.5) end
end
end
local filters = {{filter = "name", name = "rsc-silo-stage1"}, {filter = "name", name = "rsc-silo-stage1-serlp"}, {filter = "name", name = "rocket-silo"}}
script.on_event(defines.events.on_built_entity, On_Built, filters)
script.on_event(defines.events.on_robot_built_entity, On_Built, filters)
script.on_event(defines.events.script_raised_built, On_Built, filters)
function player_mined_entity(event)
local player = game.players[event.player_index]
if not player.valid then return end
local ent = event.entity
if ent.type=='tree' then
if math.random()<=global.tree_events_chance then
local the_event=pick_event({'biterzilla','volcano'})
if the_event then
local pforce = player.force
local surface = ent.surface
Create_Position_Event(the_event, surface, ent.position, pforce)
end
end
end
end
local filters = {{filter = "type", type = "tree"}}
script.on_event(defines.events.on_player_mined_entity, player_mined_entity, filters)
-- warptorio
function on_warptorio_warp(event)
global.surfaces = {}
local newsurface = event.newsurface
local planet = event.newplanet
if planet and newsurface then
local flags = planet.flags
if (not flags) or (not in_list(flags,"rest")) then
add_list(global.surfaces, newsurface.name)
end
end
end
-- player body abduction mod
function on_corpse_feasted(event)
local corpse=event.corpse
local attack=event.attack_target
if corpse and corpse.valid and attack and attack.valid then
local evo = game.forces.enemy.evolution_factor
local surface = corpse.surface
---if corpse.get_item_count("rocket-launcher")>0 then
if global.chances.biterzilla.min_evo<=evo and math.random(100) <= global.chances.biterzilla.chance*2 then
local pforce
if corpse.character_corpse_player_index and game.players[corpse.character_corpse_player_index] and game.players[corpse.character_corpse_player_index].valid then
pforce = game.players[corpse.character_corpse_player_index].force
end
local boss_name=get_random_boss_human_soldier()
local position = surface.find_non_colliding_position(boss_name, corpse.position, 30, 1)
local boss = surface.create_entity{name=boss_name, position=position, force = game.forces.enemy}
boss.ai_settings.allow_destroy_when_commands_fail=false
table.insert (global.biterzillas, {entity=boss})
if pforce then
if global.show_alerts then
pforce.print({"bm-txt-alert"},colors.lightred)
pforce.print({"bm-txt-biterzilla"},colors.lightred)
end
Play_sound_alert(pforce,3)
end
else
local humie = get_random_human_soldier()
local position = surface.find_non_colliding_position(humie, corpse.position, 30, 1)
surface.create_entity{name=humie, position= position, force= game.forces.enemy}
end
end
end
function RegisterModEvents()
if remote.interfaces["warptorio2"] then
local warp_event = remote.call("warptorio2","get_event", "on_post_warp")
script.on_event(warp_event, function(event)
on_warptorio_warp(event)
end)
end
if remote.interfaces["PlayerBodyAbduction"] then
local pba_event = remote.call("PlayerBodyAbduction", "get_on_corpse_feasted")
script.on_event(pba_event, function(event)
on_corpse_feasted(event)
end)
end
end
--------------------------------------------------------------------------------------
-- INTERFACE
--------------------------------------------------------------------------------------
local interface = {}
function interface.destroy_all()
global.invasions = {}
for _, zilla in pairs(global.biterzillas) do
if zilla.entity.valid then
zilla.entity.destroy()
end
end
global.biterzillas = {}
for k=#global.bm_volcano,1,-1 do
local V=global.bm_volcano[k]
local volcano = V.volcano
if volcano and volcano.valid then volcano.destroy() end
end
global.bm_volcano = {}
for _, e in pairs(global.other_enemies) do if e and e.valid then e.destroy() end end
global.other_enemies = {}
end
local event_names = {'biterzilla','brutals','soldiers','worms','volcano','invasion','swarm','spidertron','ultimate_boss'}
function interface.create_event(the_event,surfacename,forcename)
if (not the_event) or in_list(event_names, the_event) then
CreateNewEvent(the_event,surfacename, forcename)
end
end
function interface.create_silo_event(the_event)
if (not the_event) or in_list(event_names, the_event) then
Create_Silo_Attack(the_event)
end
end
-- exclude / include surfaces for disasters
function interface.exclude_surface(surfacename)
if surfacename then del_list(global.surfaces, surfacename) end
end
function interface.add_surface(surfacename)
if surfacename then add_list(global.surfaces, surfacename) end
end
-- exclude / include player forces to be targeted
function interface.exclude_player_force(forcename)
if forcename then del_list(global.player_forces, forcename) end
end
function interface.add_player_force(forcename)
if forcename then add_list(global.player_forces, forcename) end
end
remote.add_interface( "bigmonster", interface )
--[[
Usage for creating events: create_event(eventname,surfacename,forcename)
Use these event names 'biterzilla','soldiers','worms','volcano','invasion','swarm'
nil will create a random event
Examples:
Call specific events for all surfaces and all player forces
/c remote.call( "bigmonster", "create_event", "biterzilla")
/c remote.call( "bigmonster", "create_event", "soldiers")
/c remote.call( "bigmonster", "create_event", "worms")
/c remote.call( "bigmonster", "create_event", "volcano")
/c remote.call( "bigmonster", "create_event", "invasion")
/c remote.call( "bigmonster", "create_event", "swarm")
/c remote.call( "bigmonster", "create_event", "spidertron")
/c remote.call( "bigmonster", "create_event", "ultimate_boss")
Call specific event for nauvis surface only, to all player forces
/c remote.call( "bigmonster", "create_event", "biterzilla", "nauvis")
Call specific event for all registered surfaces, to specific player force
/c remote.call( "bigmonster", "create_event", "volcano", nil, "player")
Call random event for all registered surfaces, to specific player force
/c remote.call( "bigmonster", "create_event", nil, nil, "player")
Call an event for attacking All Silos - eventname = all above
/c remote.call( "bigmonster", "create_silo_event", eventname)
include surfaces for disasters
/c remote.call( "bigmonster", "add_surface", surfacename)
exclude surfaces for disasters
/c remote.call( "bigmonster", "exclude_surface", surfacename)
include player force to be targeted
/c remote.call( "bigmonster","add_player_force",forcename)
exclude player force to be targeted
/c remote.call( "bigmonster","exclude_player_force",forcename)
destroy all mod enemies
/c remote.call( "bigmonster","destroy_all")
]]