2019-11-09 16:46:18 +02:00
local BiterHealthBooster = require " modules.biter_health_booster "
2019-10-28 18:38:36 +02:00
local BiterRolls = require " modules.wave_defense.biter_rolls "
2019-10-29 12:26:59 +02:00
local SideTargets = require " modules.wave_defense.side_targets "
2019-10-28 18:38:36 +02:00
local ThreatEvent = require " modules.wave_defense.threat_events "
2019-10-22 10:18:28 +02:00
local update_gui = require " modules.wave_defense.gui "
2019-10-08 17:41:15 +02:00
local threat_values = require " modules.wave_defense.threat_values "
2019-10-28 18:38:36 +02:00
local WD = require " modules.wave_defense.table "
2020-06-07 13:33:24 +02:00
local Alert = require ' utils.alert '
2019-11-09 14:18:30 +02:00
local math_random = math.random
local math_floor = math.floor
local table_insert = table.insert
local math_sqrt = math.sqrt
local math_round = math.round
2019-10-28 18:38:36 +02:00
local event = require ' utils.event '
local Public = { }
2019-11-09 14:18:30 +02:00
local group_size_modifier_raffle = { }
local group_size_chances = { { 4 , 0.4 } , { 5 , 0.5 } , { 6 , 0.6 } , { 7 , 0.7 } , { 8 , 0.8 } , { 9 , 0.9 } , { 10 , 1 } , { 9 , 1.1 } , { 8 , 1.2 } , { 7 , 1.3 } , { 6 , 1.4 } , { 5 , 1.5 } , { 4 , 1.6 } , { 3 , 1.7 } , { 2 , 1.8 } }
for _ , v in pairs ( group_size_chances ) do
for c = 1 , v [ 1 ] , 1 do
table_insert ( group_size_modifier_raffle , v [ 2 ] )
end
end
local group_size_modifier_raffle_size = # group_size_modifier_raffle
2019-10-08 01:17:00 +02:00
local function debug_print ( msg )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
if not wave_defense_table.debug then return end
print ( " WaveDefense: " .. msg )
2019-10-08 01:17:00 +02:00
end
2020-04-08 20:28:02 +02:00
local function is_closer ( pos1 , pos2 , pos )
return ( ( pos1.x - pos.x ) ^ 2 + ( pos1.y - pos.y ) ^ 2 ) < ( ( pos2.x - pos.x ) ^ 2 + ( pos2.y - pos.y ) ^ 2 )
end
local function shuffle_distance ( tbl , position )
local size = # tbl
for i = size , 1 , - 1 do
local rand = math_random ( size )
if is_closer ( tbl [ i ] . position , tbl [ rand ] . position , position ) and i > rand then
tbl [ i ] , tbl [ rand ] = tbl [ rand ] , tbl [ i ]
end
end
return tbl
end
2019-10-15 04:20:40 +02:00
local function is_unit_valid ( biter )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
2019-10-08 17:41:15 +02:00
if not biter.entity then debug_print ( " is_unit_valid - unit destroyed - does no longer exist " ) return false end
if not biter.entity . valid then debug_print ( " is_unit_valid - unit destroyed - invalid " ) return false end
if not biter.entity . unit_group then debug_print ( " is_unit_valid - unit destroyed - no unitgroup " ) return false end
2019-10-28 18:38:36 +02:00
if biter.spawn_tick + wave_defense_table.max_biter_age < game.tick then debug_print ( " is_unit_valid - unit destroyed - timed out " ) return false end
2019-10-07 22:40:05 +02:00
return true
end
2019-10-25 08:09:39 +02:00
local function refresh_active_unit_threat ( )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
debug_print ( " refresh_active_unit_threat - current value " .. wave_defense_table.active_biter_threat )
2019-10-25 08:09:39 +02:00
local active_biter_threat = 0
2019-10-28 18:38:36 +02:00
for k , biter in pairs ( wave_defense_table.active_biters ) do
2019-10-25 08:09:39 +02:00
if biter.entity then
if biter.entity . valid then
active_biter_threat = active_biter_threat + threat_values [ biter.entity . name ]
end
end
end
2019-11-09 14:18:30 +02:00
wave_defense_table.active_biter_threat = math_round ( active_biter_threat * global.biter_health_boost , 2 )
2019-10-28 18:38:36 +02:00
debug_print ( " refresh_active_unit_threat - new value " .. wave_defense_table.active_biter_threat )
2019-10-25 08:09:39 +02:00
end
2019-10-14 06:52:17 +02:00
local function time_out_biters ( )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
for k , biter in pairs ( wave_defense_table.active_biters ) do
2019-10-07 22:40:05 +02:00
if not is_unit_valid ( biter ) then
2019-10-28 18:38:36 +02:00
wave_defense_table.active_biter_count = wave_defense_table.active_biter_count - 1
2019-10-07 22:40:05 +02:00
if biter.entity then
if biter.entity . valid then
2019-11-09 14:18:30 +02:00
wave_defense_table.active_biter_threat = wave_defense_table.active_biter_threat - math_round ( threat_values [ biter.entity . name ] * global.biter_health_boost , 2 )
2019-10-14 06:52:17 +02:00
if biter.entity . force.index == 2 then
biter.entity . destroy ( )
end
2019-10-07 22:40:05 +02:00
end
end
2019-10-28 18:38:36 +02:00
wave_defense_table.active_biters [ k ] = nil
2019-10-07 04:37:23 +02:00
end
end
end
2019-10-12 04:06:48 +02:00
local function get_random_close_spawner ( surface )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
2020-07-22 18:58:42 +02:00
local spawners = surface.find_entities_filtered ( { type = " unit-spawner " , force = ' enemy ' } )
2019-10-07 01:46:26 +02:00
if not spawners [ 1 ] then return false end
2019-10-28 18:38:36 +02:00
local center = wave_defense_table.target . position
2019-11-09 14:18:30 +02:00
local spawner = spawners [ math_random ( 1 , # spawners ) ]
2019-10-28 18:38:36 +02:00
for i = 1 , wave_defense_table.get_random_close_spawner_attempts , 1 do
2019-11-09 14:18:30 +02:00
local spawner_2 = spawners [ math_random ( 1 , # spawners ) ]
2019-10-28 18:38:36 +02:00
if ( center.x - spawner_2.position . x ) ^ 2 + ( center.y - spawner_2.position . y ) ^ 2 < ( center.x - spawner.position . x ) ^ 2 + ( center.y - spawner.position . y ) ^ 2 then spawner = spawner_2 end
end
2019-10-25 08:09:39 +02:00
debug_print ( " get_random_close_spawner - Found at x " .. spawner.position . x .. " y " .. spawner.position . y )
2019-10-07 01:46:26 +02:00
return spawner
end
2020-01-01 15:05:49 +02:00
local function get_random_character ( wave_defense_table )
local characters = { }
for _ , player in pairs ( game.connected_players ) do
if player.character then
if player.character . valid then
if player.character . surface.index == wave_defense_table.surface_index then
characters [ # characters + 1 ] = player.character
end
end
end
end
if not characters [ 1 ] then return end
return characters [ math_random ( 1 , # characters ) ]
end
2019-10-25 08:09:39 +02:00
local function set_main_target ( )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
if wave_defense_table.target then
if wave_defense_table.target . valid then return end
2019-10-25 08:09:39 +02:00
end
2020-04-08 20:28:02 +02:00
2020-01-01 15:05:49 +02:00
local target = SideTargets.get_side_target ( )
if not target then target = get_random_character ( wave_defense_table ) end
2019-10-25 08:09:39 +02:00
if not target then return end
2020-04-08 20:28:02 +02:00
wave_defense_table.target = target
2019-10-25 08:09:39 +02:00
debug_print ( " set_main_target -- New main target " .. target.name .. " at position x " .. target.position . x .. " y " .. target.position . y .. " selected. " )
end
2019-10-12 04:06:48 +02:00
local function set_group_spawn_position ( surface )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
2019-10-12 04:06:48 +02:00
local spawner = get_random_close_spawner ( surface )
2019-10-07 01:46:26 +02:00
if not spawner then return end
2020-01-01 15:05:49 +02:00
local position = surface.find_non_colliding_position ( " behemoth-biter " , spawner.position , 64 , 1 )
2019-10-28 18:38:36 +02:00
if not position then return end
2019-11-01 17:24:44 +02:00
wave_defense_table.spawn_position = { x = position.x , y = position.y }
2019-10-28 18:38:36 +02:00
debug_print ( " set_group_spawn_position -- Changed position to x " .. wave_defense_table.spawn_position . x .. " y " .. wave_defense_table.spawn_position . y .. " . " )
2019-10-07 01:46:26 +02:00
end
local function set_enemy_evolution ( )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
local evolution_factor = wave_defense_table.wave_number * 0.001
2019-10-23 21:17:15 +02:00
local biter_health_boost = 1
2019-10-26 11:47:46 +02:00
--local damage_increase = 0
2019-10-28 18:38:36 +02:00
2019-10-13 05:24:29 +02:00
if evolution_factor > 1 then
2019-10-26 11:47:46 +02:00
--damage_increase = damage_increase + (evolution_factor - 1)
2019-10-26 13:56:02 +02:00
--biter_health_boost = biter_health_boost + (evolution_factor - 1) * 2
2019-10-13 05:24:29 +02:00
evolution_factor = 1
end
2019-10-23 21:17:15 +02:00
2019-11-01 12:46:28 +02:00
if wave_defense_table.threat > 50000 then
2019-11-09 14:18:30 +02:00
biter_health_boost = math_round ( biter_health_boost + ( wave_defense_table.threat - 50000 ) * 0.000033 , 3 )
--damage_increase = math_round(damage_increase + wave_defense_table.threat * 0.0000025, 3)
2019-10-07 09:48:17 +02:00
end
2019-10-13 05:24:29 +02:00
2019-10-23 21:17:15 +02:00
global.biter_health_boost = biter_health_boost
2019-10-26 11:47:46 +02:00
--game.forces.enemy.set_ammo_damage_modifier("melee", damage_increase)
--game.forces.enemy.set_ammo_damage_modifier("biological", damage_increase)
2019-10-13 05:24:29 +02:00
game.forces . enemy.evolution_factor = evolution_factor
2019-10-25 08:09:39 +02:00
end
local function can_units_spawn ( )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
if wave_defense_table.threat <= 0 then
2019-10-25 08:09:39 +02:00
debug_print ( " can_units_spawn - threat too low " )
2019-10-28 18:38:36 +02:00
return false
2019-10-25 08:09:39 +02:00
end
2019-10-28 18:38:36 +02:00
if wave_defense_table.active_biter_count >= wave_defense_table.max_active_biters then
2019-10-25 08:09:39 +02:00
debug_print ( " can_units_spawn - active biter count too high " )
2019-10-28 18:38:36 +02:00
return false
2019-10-25 08:09:39 +02:00
end
2019-10-28 18:38:36 +02:00
if wave_defense_table.active_biter_threat >= wave_defense_table.threat then
debug_print ( " can_units_spawn - active biter threat too high ( " .. wave_defense_table.active_biter_threat .. " ) " )
return false
2019-10-25 08:09:39 +02:00
end
return true
end
local function get_active_unit_groups_count ( )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
2019-10-25 08:09:39 +02:00
local count = 0
2019-10-28 18:38:36 +02:00
for _ , g in pairs ( wave_defense_table.unit_groups ) do
2019-10-25 08:09:39 +02:00
if g.valid then
2019-10-26 11:47:46 +02:00
if # g.members > 0 then
count = count + 1
else
g.destroy ( )
end
2019-10-25 08:09:39 +02:00
end
end
debug_print ( " Active unit group count: " .. count )
return count
2019-10-07 01:46:26 +02:00
end
2019-11-09 16:46:18 +02:00
local function spawn_biter ( surface , is_boss_biter )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
2019-11-09 16:46:18 +02:00
if not is_boss_biter then
if not can_units_spawn ( ) then return end
end
2020-04-08 20:28:02 +02:00
2019-10-12 04:06:48 +02:00
local name
2019-11-09 14:18:30 +02:00
if math_random ( 1 , 100 ) > 73 then
2019-10-28 18:38:36 +02:00
name = BiterRolls.wave_defense_roll_spitter_name ( )
2019-10-12 04:06:48 +02:00
else
2019-10-28 18:38:36 +02:00
name = BiterRolls.wave_defense_roll_biter_name ( )
2019-10-12 04:06:48 +02:00
end
2019-11-01 17:24:44 +02:00
--local position = surface.find_non_colliding_position(name, wave_defense_table.spawn_position, 48, 2)
--if not position then return false end
local biter = surface.create_entity ( { name = name , position = wave_defense_table.spawn_position , force = " enemy " } )
2019-10-07 01:46:26 +02:00
biter.ai_settings . allow_destroy_when_commands_fail = false
biter.ai_settings . allow_try_return_to_spawner = false
2019-11-09 16:46:18 +02:00
if is_boss_biter then BiterHealthBooster.add_boss_unit ( biter , global.biter_health_boost * 5 , 0.35 ) end
2019-10-28 18:38:36 +02:00
wave_defense_table.active_biters [ biter.unit_number ] = { entity = biter , spawn_tick = game.tick }
wave_defense_table.active_biter_count = wave_defense_table.active_biter_count + 1
2019-11-09 14:18:30 +02:00
wave_defense_table.active_biter_threat = wave_defense_table.active_biter_threat + math_round ( threat_values [ name ] * global.biter_health_boost , 2 )
2019-10-14 06:52:17 +02:00
return biter
2019-10-07 01:46:26 +02:00
end
2019-10-07 22:40:05 +02:00
local function set_next_wave ( )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
wave_defense_table.wave_number = wave_defense_table.wave_number + 1
2020-04-08 20:28:02 +02:00
2019-10-30 18:29:15 +02:00
local threat_gain = wave_defense_table.wave_number * wave_defense_table.threat_gain_multiplier
2019-11-01 12:46:28 +02:00
if wave_defense_table.wave_number > 1000 then
threat_gain = threat_gain * ( wave_defense_table.wave_number * 0.001 )
end
2019-11-09 16:46:18 +02:00
if wave_defense_table.wave_number % 25 == 0 then
wave_defense_table.boss_wave = true
2020-06-05 18:00:57 +02:00
wave_defense_table.boss_wave_warning = true
2020-06-07 13:33:24 +02:00
if wave_defense_table.alert_boss_wave then
local msg = ' Boss Wave: ' .. wave_defense_table.wave_number
local pos = {
position = wave_defense_table.spawn_position
}
Alert.alert_all_players_location ( pos , msg , { r = 0.8 , g = 0.1 , b = 0.1 } )
end
2020-04-08 20:28:02 +02:00
threat_gain = threat_gain * 2
2020-06-05 18:00:57 +02:00
else
if wave_defense_table.boss_wave_warning then
wave_defense_table.boss_wave_warning = false
end
2019-11-09 16:46:18 +02:00
end
2020-04-08 20:28:02 +02:00
wave_defense_table.threat = wave_defense_table.threat + math_floor ( threat_gain )
2019-10-28 18:38:36 +02:00
wave_defense_table.last_wave = wave_defense_table.next_wave
wave_defense_table.next_wave = game.tick + wave_defense_table.wave_interval
2020-06-05 18:00:57 +02:00
if wave_defense_table.clear_corpses then
local surface = game.surfaces [ wave_defense_table.surface_index ]
for _ , entity in pairs ( surface.find_entities_filtered { type = ' corpse ' } ) do
if math_random ( 1 , 2 ) == 1 then
entity.destroy ( )
end
end
end
2019-10-07 22:40:05 +02:00
end
2020-04-27 15:50:18 +02:00
local function reform_group ( group )
local wave_defense_table = WD.get_table ( )
local group_position = { x = group.position . x , y = group.position . y }
local step_length = wave_defense_table.unit_group_command_step_length
local position = group.surface . find_non_colliding_position ( " biter-spawner " , group_position , step_length , 4 )
if position then
local new_group = group.surface . create_unit_group { position = position , force = group.force }
for key , biter in pairs ( group.members ) do
new_group.add_member ( biter )
end
debug_print ( " Creating new unit group, because old one was stuck. " )
table_insert ( wave_defense_table.unit_groups , new_group )
return new_group
else
debug_print ( " Destroying stuck group. " )
--table.remove(wave_defense_table.unit_groups, group) --need group id instead to work, so as of now, groups are removed only by regular remove checks :( !
group.destroy ( )
end
return nil
end
2019-10-08 01:17:00 +02:00
local function get_commmands ( group )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
2019-10-08 01:17:00 +02:00
local commands = { }
local group_position = { x = group.position . x , y = group.position . y }
2019-10-28 18:38:36 +02:00
local step_length = wave_defense_table.unit_group_command_step_length
2019-11-09 14:18:30 +02:00
if math_random ( 1 , 2 ) == 1 then
2019-10-29 12:26:59 +02:00
local side_target = SideTargets.get_side_target ( )
2019-10-11 21:52:32 +02:00
if side_target then
2019-10-28 18:38:36 +02:00
local target_position = side_target.position
2019-11-09 14:18:30 +02:00
local distance_to_target = math_floor ( math_sqrt ( ( target_position.x - group_position.x ) ^ 2 + ( target_position.y - group_position.y ) ^ 2 ) )
local steps = math_floor ( distance_to_target / step_length ) + 1
local vector = { math_round ( ( target_position.x - group_position.x ) / steps , 3 ) , math_round ( ( target_position.y - group_position.y ) / steps , 3 ) }
2019-10-28 18:38:36 +02:00
if wave_defense_table.debug then
2019-10-25 08:09:39 +02:00
debug_print ( " get_commmands - to side_target x " .. side_target.position . x .. " y " .. side_target.position . y )
debug_print ( " get_commmands - distance_to_target: " .. distance_to_target .. " steps: " .. steps )
debug_print ( " get_commmands - vector " .. vector [ 1 ] .. " _ " .. vector [ 2 ] )
2019-10-15 04:20:40 +02:00
end
2019-10-28 18:38:36 +02:00
2019-10-11 21:52:32 +02:00
for i = 1 , steps , 1 do
2020-04-08 20:28:02 +02:00
local old_position = group_position
2019-10-11 21:52:32 +02:00
group_position.x = group_position.x + vector [ 1 ]
2020-04-08 20:28:02 +02:00
group_position.y = group_position.y + vector [ 2 ]
local obstacles = group.surface . find_entities_filtered { position = old_position , radius = step_length , type = { " simple-entity " , " tree " } , limit = 50 }
if obstacles then
shuffle_distance ( obstacles , old_position )
for i = 1 , # obstacles , 1 do
if obstacles [ i ] . valid then
commands [ # commands + 1 ] = {
type = defines.command . attack ,
target = obstacles [ i ] ,
distraction = defines.distraction . by_enemy
}
end
end
end
local position = group.surface . find_non_colliding_position ( " rocket-silo " , group_position , step_length , 4 )
2019-10-11 21:52:32 +02:00
if position then
2020-04-08 20:28:02 +02:00
-- commands[#commands + 1] = {
-- type = defines.command.go_to_location,
-- destination = {x = position.x, y = position.y},
-- distraction = defines.distraction.by_anything
-- }
2019-10-11 21:52:32 +02:00
commands [ # commands + 1 ] = {
type = defines.command . attack_area ,
destination = { x = position.x , y = position.y } ,
radius = 16 ,
distraction = defines.distraction . by_anything
}
2020-04-08 20:28:02 +02:00
else
local obstacles = group.surface . find_entities_filtered { position = group_position , radius = step_length , type = { " simple-entity " , " tree " } , limit = 50 }
if obstacles then
shuffle_distance ( obstacles , old_position )
for i = 1 , # obstacles , 1 do
if obstacles [ i ] . valid then
commands [ # commands + 1 ] = {
type = defines.command . attack ,
target = obstacles [ i ] ,
distraction = defines.distraction . by_enemy
}
end
end
end
2019-10-28 18:38:36 +02:00
end
2019-10-11 21:52:32 +02:00
end
2019-10-28 18:38:36 +02:00
2019-10-11 21:52:32 +02:00
commands [ # commands + 1 ] = {
type = defines.command . attack ,
target = side_target ,
distraction = defines.distraction . by_enemy ,
2019-10-28 18:38:36 +02:00
}
2019-10-10 01:42:38 +02:00
end
end
2019-10-28 18:38:36 +02:00
local target_position = wave_defense_table.target . position
2019-11-09 14:18:30 +02:00
local distance_to_target = math_floor ( math_sqrt ( ( target_position.x - group_position.x ) ^ 2 + ( target_position.y - group_position.y ) ^ 2 ) )
local steps = math_floor ( distance_to_target / step_length ) + 1
local vector = { math_round ( ( target_position.x - group_position.x ) / steps , 3 ) , math_round ( ( target_position.y - group_position.y ) / steps , 3 ) }
2019-10-28 18:38:36 +02:00
if wave_defense_table.debug then
2019-10-25 08:09:39 +02:00
debug_print ( " get_commmands - to main target x " .. target_position.x .. " y " .. target_position.y )
debug_print ( " get_commmands - distance_to_target: " .. distance_to_target .. " steps: " .. steps )
debug_print ( " get_commmands - vector " .. vector [ 1 ] .. " _ " .. vector [ 2 ] )
2019-10-15 04:20:40 +02:00
end
2019-10-28 18:38:36 +02:00
2019-10-08 01:17:00 +02:00
for i = 1 , steps , 1 do
2020-04-08 20:28:02 +02:00
local old_position = group_position
2019-10-08 01:17:00 +02:00
group_position.x = group_position.x + vector [ 1 ]
2019-10-28 18:38:36 +02:00
group_position.y = group_position.y + vector [ 2 ]
2020-04-08 20:28:02 +02:00
local obstacles = group.surface . find_entities_filtered { position = old_position , radius = step_length / 2 , type = { " simple-entity " , " tree " } , limit = 50 }
if obstacles then
shuffle_distance ( obstacles , old_position )
for i = 1 , # obstacles , 1 do
if obstacles [ i ] . valid then
commands [ # commands + 1 ] = {
type = defines.command . attack ,
target = obstacles [ i ] ,
distraction = defines.distraction . by_enemy
}
end
end
end
local position = group.surface . find_non_colliding_position ( " rocket-silo " , group_position , step_length , 1 )
2019-10-08 01:17:00 +02:00
if position then
commands [ # commands + 1 ] = {
type = defines.command . attack_area ,
destination = { x = position.x , y = position.y } ,
radius = 16 ,
2019-10-08 05:50:32 +02:00
distraction = defines.distraction . by_anything
2019-10-08 01:17:00 +02:00
}
2019-10-28 18:38:36 +02:00
end
2019-10-08 01:17:00 +02:00
end
2019-10-28 18:38:36 +02:00
2019-10-08 01:17:00 +02:00
commands [ # commands + 1 ] = {
type = defines.command . attack_area ,
2019-10-18 07:01:14 +02:00
destination = { x = target_position.x , y = target_position.y } ,
2019-10-08 01:17:00 +02:00
radius = 8 ,
distraction = defines.distraction . by_enemy
}
2019-10-28 18:38:36 +02:00
2019-10-08 01:17:00 +02:00
commands [ # commands + 1 ] = {
type = defines.command . attack ,
2019-10-28 18:38:36 +02:00
target = wave_defense_table.target ,
2019-10-08 01:17:00 +02:00
distraction = defines.distraction . by_enemy ,
}
2019-10-28 18:38:36 +02:00
2019-10-08 01:17:00 +02:00
return commands
end
2019-10-07 22:40:05 +02:00
local function command_unit_group ( group )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
if not wave_defense_table.unit_group_last_command [ group.group_number ] then
wave_defense_table.unit_group_last_command [ group.group_number ] = game.tick - ( wave_defense_table.unit_group_command_delay + 1 )
end
if wave_defense_table.unit_group_last_command [ group.group_number ] then
2020-04-08 20:28:02 +02:00
if wave_defense_table.unit_group_last_command [ group.group_number ] + wave_defense_table.unit_group_command_delay > game.tick then return end
2019-10-16 00:23:54 +02:00
end
2020-04-27 15:50:18 +02:00
local tile = group.surface . get_tile ( group.position )
if tile.valid and tile.collides_with ( " player-layer " ) then
group = reform_group ( group )
end
if not group then return end
2020-04-08 20:28:02 +02:00
2019-10-07 22:40:05 +02:00
group.set_command ( {
type = defines.command . compound ,
structure_type = defines.compound_command . return_last ,
2019-10-08 01:17:00 +02:00
commands = get_commmands ( group )
2019-10-07 22:40:05 +02:00
} )
2020-04-08 20:28:02 +02:00
2019-10-28 18:38:36 +02:00
wave_defense_table.unit_group_last_command [ group.group_number ] = game.tick
2019-10-07 22:40:05 +02:00
end
2019-10-15 04:20:40 +02:00
local function give_commands_to_unit_groups ( )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
if # wave_defense_table.unit_groups == 0 then return end
if not wave_defense_table.target then return end
if not wave_defense_table.target . valid then return end
for k , group in pairs ( wave_defense_table.unit_groups ) do
2019-10-15 04:20:40 +02:00
if group.valid then
command_unit_group ( group )
else
2019-10-28 18:38:36 +02:00
table.remove ( wave_defense_table.unit_groups , k )
2019-10-15 04:20:40 +02:00
end
2019-10-28 18:38:36 +02:00
end
2019-10-07 04:37:23 +02:00
end
2019-10-27 20:26:55 +02:00
local function spawn_unit_group ( )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
2019-10-27 20:26:55 +02:00
if not can_units_spawn ( ) then return end
2019-10-28 18:38:36 +02:00
if not wave_defense_table.target then return end
if not wave_defense_table.target . valid then return end
if get_active_unit_groups_count ( ) >= wave_defense_table.max_active_unit_groups then return end
2019-11-14 19:48:03 +02:00
local surface = game.surfaces [ wave_defense_table.surface_index ]
set_group_spawn_position ( surface )
2019-11-09 12:24:39 +02:00
local pos = wave_defense_table.spawn_position
2019-11-14 19:48:03 +02:00
if not surface.can_place_entity ( { name = " small-biter " , position = pos } ) then return end
2020-04-08 20:28:02 +02:00
2019-11-14 21:08:53 +02:00
local radius = 10
local area = { left_top = { pos.x - radius , pos.y - radius } , right_bottom = { pos.x + radius , pos.y + radius } }
for k , v in pairs ( surface.find_entities_filtered { area = area , name = " land-mine " } ) do if v and v.valid then v.die ( ) end end
2020-04-08 20:28:02 +02:00
2019-10-28 18:38:36 +02:00
BiterRolls.wave_defense_set_unit_raffle ( wave_defense_table.wave_number )
2020-04-08 20:28:02 +02:00
2019-10-28 18:38:36 +02:00
debug_print ( " Spawning unit group at x " .. wave_defense_table.spawn_position . x .. " y " .. wave_defense_table.spawn_position . y )
2020-04-08 20:28:02 +02:00
local unit_group = surface.create_unit_group ( { position = wave_defense_table.spawn_position , force = " enemy " } )
2019-11-09 14:18:30 +02:00
local group_size = math_floor ( wave_defense_table.average_unit_group_size * group_size_modifier_raffle [ math_random ( 1 , group_size_modifier_raffle_size ) ] )
2019-11-09 16:46:18 +02:00
for _ = 1 , group_size , 1 do
2019-10-27 20:26:55 +02:00
local biter = spawn_biter ( surface )
if not biter then break end
unit_group.add_member ( biter )
2019-11-09 16:46:18 +02:00
end
2020-04-08 20:28:02 +02:00
2019-11-09 16:46:18 +02:00
if wave_defense_table.boss_wave then
local count = math_random ( 1 , math_floor ( wave_defense_table.wave_number * 0.01 ) + 2 )
if count > 8 then count = 8 end
for _ = 1 , count , 1 do
local biter = spawn_biter ( surface , true )
if not biter then break end
unit_group.add_member ( biter )
end
wave_defense_table.boss_wave = false
end
2020-04-08 20:28:02 +02:00
table_insert ( wave_defense_table.unit_groups , unit_group )
2019-10-27 20:26:55 +02:00
return true
end
2019-10-28 14:29:15 +02:00
local function log_threat ( )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
wave_defense_table.threat_log_index = wave_defense_table.threat_log_index + 1
wave_defense_table.threat_log [ wave_defense_table.threat_log_index ] = wave_defense_table.threat
2020-04-08 20:28:02 +02:00
if wave_defense_table.threat_log_index > 900 then wave_defense_table.threat_log [ wave_defense_table.threat_log_index - 901 ] = nil end
2019-10-28 14:29:15 +02:00
end
2019-10-15 04:20:40 +02:00
local tick_tasks = {
[ 30 ] = set_main_target ,
[ 60 ] = set_enemy_evolution ,
2019-10-25 08:09:39 +02:00
[ 90 ] = spawn_unit_group ,
2019-10-15 04:20:40 +02:00
[ 120 ] = give_commands_to_unit_groups ,
2019-10-28 18:38:36 +02:00
[ 150 ] = ThreatEvent.build_nest ,
[ 180 ] = ThreatEvent.build_worm ,
2019-10-25 08:09:39 +02:00
[ 3600 ] = time_out_biters ,
[ 7200 ] = refresh_active_unit_threat ,
2019-10-15 04:20:40 +02:00
}
2019-10-07 01:46:26 +02:00
local function on_tick ( )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
if wave_defense_table.game_lost then return end
2020-04-08 20:28:02 +02:00
2019-10-28 18:38:36 +02:00
if game.tick > wave_defense_table.next_wave then set_next_wave ( ) end
2019-10-15 04:20:40 +02:00
local t = game.tick % 300
2019-10-25 08:09:39 +02:00
local t2 = game.tick % 18000
2020-04-08 20:28:02 +02:00
2019-10-15 04:20:40 +02:00
if tick_tasks [ t ] then tick_tasks [ t ] ( ) end
2019-10-25 08:09:39 +02:00
if tick_tasks [ t2 ] then tick_tasks [ t2 ] ( ) end
2020-04-08 20:28:02 +02:00
2019-10-28 14:29:15 +02:00
if game.tick % 60 == 0 then log_threat ( ) end
for _ , player in pairs ( game.connected_players ) do update_gui ( player ) end
2019-10-07 01:46:26 +02:00
end
2019-10-07 16:40:52 +02:00
local function on_init ( )
2019-10-28 18:38:36 +02:00
local wave_defense_table = WD.get_table ( )
wave_defense_table.reset_wave_defense ( )
2019-10-07 16:40:52 +02:00
end
2019-10-07 01:46:26 +02:00
2019-10-07 04:37:23 +02:00
event.on_nth_tick ( 30 , on_tick )
2020-04-08 20:28:02 +02:00
return Public