You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

870 lines
36 KiB

local Meteor = {}
Meteor.name_meteor_defence = mod_prefix.."meteor-defence"
Meteor.name_meteor_defence_container = mod_prefix.."meteor-defence-container"
Meteor.name_meteor_defence_charger = mod_prefix.."meteor-defence-charger"
Meteor.name_meteor_defence_ammo = mod_prefix.."meteor-defence-ammo"
Meteor.name_meteor_defence_beam = mod_prefix.."meteor-defence-beam"
Meteor.name_meteor_defence_beam_offset = {x = -1, y = -5}
Meteor.name_meteor_point_defence = mod_prefix.."meteor-point-defence"
Meteor.name_meteor_point_defence_container = mod_prefix.."meteor-point-defence-container"
Meteor.name_meteor_point_defence_charger = mod_prefix.."meteor-point-defence-charger"
Meteor.name_meteor_point_defence_charger_overcharged = mod_prefix.."meteor-point-defence-charger-overcharged"
Meteor.name_meteor_point_defence_ammo = mod_prefix.."meteor-point-defence-ammo"
Meteor.name_meteor_point_defence_beam = mod_prefix.."meteor-point-defence-beam"
Meteor.name_meteor_point_defence_mask = mod_prefix.."meteor-point-defence-mask"
Meteor.name_meteor_point_defence_beam_offsets = {
{x = 0.1, y = -3.5},
{x = 0.4, y = -3.1},
{x = -0.3, y = -3.4},
{x = 0.7, y = -3.2},
}
Meteor.defence_accuracy = 0.8
Meteor.defence_full_power = 2000 * 1000000
Meteor.defence_passive_draw = 5000000
Meteor.point_defence_range = 64
Meteor.point_defence_accuracy = 0.5
Meteor.point_defence_full_power = 200 * 1000000
Meteor.point_defence_fire_power = Meteor.point_defence_full_power / 4
Meteor.point_defence_passive_draw = 500000
Meteor.point_defence_passive_draw_overcharged = 250000
Meteor.meteor_swarm_altitude = 100
Meteor.meteor_swarm_x_deviation = Meteor.meteor_swarm_altitude
Meteor.meteor_fall_time = 2 * 60
Meteor.meteor_chain_delay = 10
Meteor.meteor_random_range = 512
Meteor.meteor_variants = 16
Meteor.meteor_position_deviation = 10
Meteor.meteor_chain_distance = 10
Meteor.tick_skip = 10
function Meteor.on_player_selected_or_opened_changed(event)
local player = game.players[event.player_index]
local playerdata = get_make_playerdata(player)
if playerdata.meteor_point_defence_radius then
if not (
(player.selected and player.selected.name == Meteor.name_meteor_point_defence_container)
or (player.opened and player.opened_gui_type == defines.gui_type.entity and player.opened.name == Meteor.name_meteor_point_defence_container) ) then
if rendering.is_valid(playerdata.meteor_point_defence_radius) then
rendering.destroy(playerdata.meteor_point_defence_radius)
end
playerdata.meteor_point_defence_radius = nil
end
end
end
Event.addListener(defines.events.on_gui_opened, Meteor.on_player_selected_or_opened_changed)
Event.addListener(defines.events.on_selected_entity_changed, Meteor.on_player_selected_or_opened_changed)
function Meteor.on_entity_created(event)
local entity = util.get_entity_from_event(event)
if not entity then return end
if entity.name == Meteor.name_meteor_defence_container
or entity.name == Meteor.name_meteor_point_defence_container then
local zone = Zone.from_surface(entity.surface)
if zone then
entity.active = false
local old_entities = entity.surface.find_entities_filtered{
name = {Meteor.name_meteor_defence_charger, Meteor.name_meteor_point_defence_charger},
area = entity.bounding_box,
}
for _, old_entity in pairs(old_entities) do
old_entity.destroy()
end
if zone.type ~= "spaceship" then
if entity.name == Meteor.name_meteor_defence_container then
local charger = entity.surface.create_entity{
name = Meteor.name_meteor_defence_charger,
position = entity.position,
direction = entity.direction,
force = entity.force
}
charger.destructible = false
if not zone.meteor_defences then
zone.meteor_defences = {}
end
zone.meteor_defences[entity.unit_number] = {
unit_number = entity.unit_number,
container = entity,
charger = charger,
}
else
local charger = entity.surface.create_entity{
name = Meteor.name_meteor_point_defence_charger,
position = entity.position,
direction = entity.direction,
force = entity.force
}
charger.destructible = false
charger.energy = 0
if not zone.meteor_point_defences then
zone.meteor_point_defences = {}
end
zone.meteor_point_defences[entity.unit_number] = {
unit_number = entity.unit_number,
container = entity,
charger = charger,
}
end
-- add to list of zones where meteors can appear
global.meteor_zones = global.meteor_zones or {}
global.meteor_zones[zone.index] = zone
end
else
cancel_entity_creation(entity, event.player_index, {"space-exploration.construction-denied"})
return
end
elseif entity.type == "electric-pole" or entity.type == "assembling-machine" then
local zone = Zone.from_surface(entity.surface)
if zone and zone.type ~= "spaceship" then
global.meteor_zones = global.meteor_zones or {}
global.meteor_zones[zone.index] = zone
end
end
end
Event.addListener(defines.events.on_entity_cloned, Meteor.on_entity_created)
Event.addListener(defines.events.on_built_entity, Meteor.on_entity_created)
Event.addListener(defines.events.on_robot_built_entity, Meteor.on_entity_created)
Event.addListener(defines.events.script_raised_built, Meteor.on_entity_created)
Event.addListener(defines.events.script_raised_revive, Meteor.on_entity_created)
function Meteor.on_removed_entity(event)
if event.entity and event.entity.valid and event.entity.surface then
if event.entity.name == Meteor.name_meteor_defence_charger then
local entity = event.entity
local charger = entity.surface.find_entity(Meteor.name_meteor_defence_container, entity.position)
if charger then charger.destroy() end
elseif event.entity.name == Meteor.name_meteor_defence_container then
local entity = event.entity
local charger = entity.surface.find_entity(Meteor.name_meteor_defence_charger, entity.position)
if charger then charger.destroy() end
elseif event.entity.name == Meteor.name_meteor_point_defence_charger or Meteor.name_meteor_point_defence_charger.."-overcharged" then
local entity = event.entity
local charger = entity.surface.find_entity(Meteor.name_meteor_point_defence_container, entity.position)
if charger then charger.destroy() end
elseif event.entity.name == Meteor.name_meteor_point_defence_container then
local entity = event.entity
local charger = entity.surface.find_entity(Meteor.name_meteor_point_defence_charger, entity.position)
if charger then charger.destroy() else
charger = entity.surface.find_entity(Meteor.name_meteor_point_defence_charger.."-overcharged", entity.position)
if charger then charger.destroy() end
end
end
end
end
Event.addListener(defines.events.on_entity_died, Meteor.on_removed_entity)
Event.addListener(defines.events.on_robot_mined_entity, Meteor.on_removed_entity)
Event.addListener(defines.events.on_player_mined_entity, Meteor.on_removed_entity)
Event.addListener(defines.events.script_raised_destroy, Meteor.on_removed_entity)
function Meteor.tick_meteor_shower(meteor_shower)
if type(meteor_shower.remaining_meteors) ~= "table" then
meteor_shower.valid = false
return
end
if meteor_shower.skip and meteor_shower.skip > 0 then
meteor_shower.skip = meteor_shower.skip - Meteor.tick_skip
return
end
if #meteor_shower.remaining_meteors == 0 then
-- no meteors left, defences won, vistory message
meteor_shower.valid = false
Meteor.meteor_shower_alert(meteor_shower)
return
end
local surface = Zone.get_make_surface(meteor_shower.zone)
if meteor_shower.zone.meteor_defences then
for _, defence in pairs(meteor_shower.zone.meteor_defences) do
local charger = defence.charger
if defence.charger and defence.charger.valid and defence.container and defence.container.valid then
if charger.energy >= Meteor.defence_full_power * 0.99 then
local inv = defence.container.get_inventory(defines.inventory.turret_ammo)
--local contents = inv.get_contents()
--if contents[Meteor.name_meteor_defence_ammo] and contents[Meteor.name_meteor_defence_ammo] > 0 then
if inv.get_item_count(Meteor.name_meteor_defence_ammo) > 0 then
-- this defence is ready to fire
local take = 1
inv.remove({name=Meteor.name_meteor_defence_ammo, count=take})
charger.force.item_production_statistics.on_flow(Meteor.name_meteor_defence_ammo, -take)
charger.energy = 0
meteor_shower.skip = 30
meteor_shower.defences_activated = meteor_shower.defences_activated + 1
if math.random() < Meteor.defence_accuracy then
Meteor.credit_kill(defence.container, mod_prefix .. "static-meteor-01")
meteor_shower.remaining_meteors[math.random(#meteor_shower.remaining_meteors)] = nil
local remaining_meteors = {}
for _, meteor in pairs(meteor_shower.remaining_meteors) do
table.insert(remaining_meteors, meteor)
end
meteor_shower.remaining_meteors = remaining_meteors
end
surface.create_entity{
name = Meteor.name_meteor_defence_beam,
position = Util.vectors_add(charger.position, Meteor.name_meteor_defence_beam_offset),
target = Util.vectors_add(charger.position, {x = 0, y = -Meteor.meteor_swarm_altitude})
}
return
end
end
end
end
end
-- try point defence
if meteor_shower.zone.meteor_point_defences then
for _, defence in pairs(meteor_shower.zone.meteor_point_defences) do
local charger = defence.charger
if defence.charger and defence.charger.valid and defence.container and defence.container.valid then
for remaining_index, meteor in pairs(meteor_shower.remaining_meteors) do
if charger.energy >= Meteor.point_defence_fire_power and Util.vectors_delta_length(meteor.land_position, defence.charger.position) <= Meteor.point_defence_range then
local inv = defence.container.get_inventory(defines.inventory.turret_ammo)
--local contents = inv.get_contents()
--if contents[Meteor.name_meteor_point_defence_ammo] and contents[Meteor.name_meteor_point_defence_ammo] > 0 then
if inv.get_item_count(Meteor.name_meteor_point_defence_ammo) > 0 then
-- this defence is ready to fire
local take = 1
inv.remove({name=Meteor.name_meteor_point_defence_ammo, count=take})
charger.force.item_production_statistics.on_flow(Meteor.name_meteor_point_defence_ammo, -take)
charger.energy = charger.energy - Meteor.point_defence_fire_power
defence.barrel = (defence.barrel or 0) % 4 + 1
surface.create_entity{
name = Meteor.name_meteor_point_defence_beam,
position = Util.vectors_add(charger.position, Meteor.name_meteor_point_defence_beam_offsets[defence.barrel]),
target = Util.vectors_add(charger.position, {x = 0, y = -Meteor.meteor_swarm_altitude})
}
meteor_shower.point_defences_activated = meteor_shower.point_defences_activated + 1
if math.random() < Meteor.point_defence_accuracy then
Meteor.credit_kill(defence.container, mod_prefix .. "static-meteor-01")
meteor_shower.remaining_meteors[remaining_index] = nil
local remaining_meteors = {}
for _, meteor in pairs(meteor_shower.remaining_meteors) do
table.insert(remaining_meteors, meteor)
end
meteor_shower.remaining_meteors = remaining_meteors
end
return
end
end
end
end
end
end
if meteor_shower.defences_activated or meteor_shower.point_defences_activated > 0 then
-- there were defences, meteors win, show defence status message
Meteor.meteor_shower_alert(meteor_shower)
end
meteor_shower.valid = false
Meteor.spawn_meteor_shower(meteor_shower)
end
function Meteor.defence_vs_projectile(projectile)
-- projectile should have the following properties:
-- projectile.force_name
-- projectile.target_position
local zone = projectile.target_zone
projectile.health = projectile.health or 1 -- some may take multiple hits?
local surface = Zone.get_make_surface(zone)
local defence_data = {}
if zone.meteor_defences then
for _, defence in pairs(zone.meteor_defences) do
local charger = defence.charger
if charger and charger.valid and not (projectile.force_name == charger.force.name or charger.force.get_cease_fire(projectile.force_name) or charger.force.get_friend(projectile.force_name)) then
defence_data[charger.force.name] = defence_data[charger.force.name] or {defence_shots = 0, point_defence_shots = 0}
if defence.container and defence.container.valid then
if charger.energy >= Meteor.defence_full_power * 0.99 then
local inv = defence.container.get_inventory(defines.inventory.turret_ammo)
if inv.get_item_count(Meteor.name_meteor_defence_ammo) > 0 then
-- this defence is ready to fire
local take = 1
inv.remove({name=Meteor.name_meteor_defence_ammo, count=take})
charger.force.item_production_statistics.on_flow(Meteor.name_meteor_defence_ammo, -take)
charger.energy = 0
defence_data[charger.force.name].defence_shots = defence_data[charger.force.name].defence_shots + 1
surface.create_entity{
name = Meteor.name_meteor_defence_beam,
position = Util.vectors_add(charger.position, Meteor.name_meteor_defence_beam_offset),
target = Util.vectors_add(charger.position, {x = 0, y = -Meteor.meteor_swarm_altitude})
}
if math.random() < Meteor.defence_accuracy then
projectile.health = projectile.health - 1
if projectile.health <= 0 then
--TODO: credit kill of a summy delivery cannon capsule.
return defence_data
end
end
end
end
end
end
end
end
-- try point defence
if zone.meteor_point_defences then
for _, defence in pairs(zone.meteor_point_defences) do
local charger = defence.charger
if charger and charger.valid and not (projectile.force_name == charger.force.name or charger.force.get_cease_fire(projectile.force_name) or charger.force.get_friend(projectile.force_name)) then
defence_data[charger.force.name] = defence_data[charger.force.name] or {defence_shots = 0, point_defence_shots = 0}
if defence.container and defence.container.valid then
if Util.vectors_delta_length(projectile.target_position, defence.charger.position) <= Meteor.point_defence_range then
local inv = defence.container.get_inventory(defines.inventory.turret_ammo)
while charger.energy >= Meteor.point_defence_fire_power and inv.get_item_count(Meteor.name_meteor_point_defence_ammo) > 0 do
-- this defence is ready to fire
local take = 1
inv.remove({name=Meteor.name_meteor_point_defence_ammo, count=take})
charger.force.item_production_statistics.on_flow(Meteor.name_meteor_point_defence_ammo, -take)
charger.energy = charger.energy - Meteor.point_defence_fire_power
defence_data[charger.force.name].point_defence_shots = defence_data[charger.force.name].point_defence_shots + 1
defence.barrel = (defence.barrel or 0) % 4 + 1
surface.create_entity{
name = Meteor.name_meteor_point_defence_beam,
position = Util.vectors_add(charger.position, Meteor.name_meteor_point_defence_beam_offsets[defence.barrel]),
target = Util.vectors_add(charger.position, {x = 0, y = -Meteor.meteor_swarm_altitude})
}
if math.random() < Meteor.point_defence_accuracy then
projectile.health = projectile.health - 1
if projectile.health <= 0 then
--TODO: credit kill of a summy delivery cannon capsule.
return defence_data
end
end
end
end
end
end
end
end
return defence_data
end
function Meteor.meteor_shower_alert(meteor_shower)
local meteor_defences = 0
local meteor_defences_ready = 0
local meteor_defences_shot_fired = meteor_shower.defences_activated
local meteor_point_defences = 0
local meteor_point_defences_ready = 0
local meteor_point_defence_shots_fired = meteor_shower.point_defences_activated
if meteor_shower.zone.meteor_defences then
for _, defence in pairs(meteor_shower.zone.meteor_defences) do
meteor_defences = meteor_defences + 1
local charger = defence.charger
if defence.charger and defence.charger.valid and defence.container and defence.container.valid then
if charger.energy >= Meteor.defence_full_power * 0.99 then
local inv = defence.container.get_inventory(defines.inventory.turret_ammo)
local contents = inv.get_contents()
if contents[Meteor.name_meteor_defence_ammo] and contents[Meteor.name_meteor_defence_ammo] > 0 then
meteor_defences_ready = meteor_defences_ready + 1
end
end
end
end
end
-- try point defence
if meteor_shower.zone.meteor_point_defences then
for _, defence in pairs(meteor_shower.zone.meteor_point_defences) do
meteor_point_defences = meteor_point_defences + 1
local charger = defence.charger
if defence.charger and defence.charger.valid and defence.container and defence.container.valid then
if charger.energy >= Meteor.point_defence_fire_power then
local inv = defence.container.get_inventory(defines.inventory.turret_ammo)
local contents = inv.get_contents()
if contents[Meteor.name_meteor_point_defence_ammo] and contents[Meteor.name_meteor_point_defence_ammo] > 0 then
meteor_point_defences_ready = meteor_point_defences_ready + 1
end
end
end
end
end
local surface = Zone.get_make_surface(meteor_shower.zone)
local dummy = surface.create_entity{name = mod_prefix.."dummy-explosion", position = meteor_shower.land_position}
local message = {"space-exploration.meteor_shower_report",
(#meteor_shower.meteors - #meteor_shower.remaining_meteors),
#meteor_shower.meteors,
"[gps="..math.floor(meteor_shower.land_position.x)..","..math.floor(meteor_shower.land_position.y)..","..surface.name.."]",
meteor_defences_shot_fired,
meteor_defences_ready,
meteor_defences,
meteor_point_defence_shots_fired,
meteor_point_defences_ready,
meteor_point_defences
}
for _, player in pairs(game.connected_players) do
if player.surface.index == surface.index then
player.add_custom_alert( dummy, {type = "virtual", name = "se-meteor"}, message, true)
if settings.get_player_settings(player)["se-print-meteor-info"].value then
player.print(message)
end
end
end
end
function Meteor.begin_meteor_shower(zone, position, range, force_meteor_count)
if not zone then return end
local surface = Zone.get_make_surface(zone)
if not position then position = {x = 0, y = 0} end
if not range then range = Meteor.meteor_random_range end
local meteor_count = 1
if force_meteor_count then
meteor_count = force_meteor_count
if force_meteor_count > 100 then
game.print("Meteor shower count capped at 100, use multiple meteor showers to bypass this limit.")
end
else
local continue = true
while continue and meteor_count < 100 do
if math.random() < 0.5 then
meteor_count = meteor_count + 1
else
continue = false
end
end
end
meteor_count = math.min(meteor_count, 100)
local x_offset = Meteor.meteor_swarm_x_deviation * 2 * (math.random() - 0.5)
local land_position = {
x = position.x + (math.random() - 0.5) * range * 2,
y = position.y + (math.random() - 0.5) * range * 2
}
local start_position = {
x = land_position.x + x_offset,
y = land_position.y - Meteor.meteor_swarm_altitude
}
local shadow_start_position = {
x = land_position.x + Meteor.meteor_swarm_altitude + x_offset,
y = land_position.y
}
local chain_angle = math.random() * 360
local chain = 0
local meteors = {}
for chain = 1, meteor_count do
meteor = {
id = chain,
safe = false
}
if chain == 1 then
meteor.land_position = {
x = land_position.x,
y = land_position.y,
}
else
meteor.land_position = {
x = land_position.x + (math.random() - 0.5) * Meteor.meteor_position_deviation,
y = land_position.y + (math.random() - 0.5) * Meteor.meteor_position_deviation,
}
meteor.land_position = util.vectors_add(meteor.land_position, util.rotate_vector(chain_angle, {x = 0, y = Meteor.meteor_chain_distance * ((chain - 1) + 0.7 * math.random()) }))
end
table.insert(meteors, meteor)
end
local defences = zone.meteor_defences or {}
global.meteor_showers = global.meteor_showers or {}
local meteor_shower = {
valid = true,
type = "meteor-shower",
zone = zone,
land_position = land_position,
start_position = start_position,
shadow_start_position = shadow_start_position,
meteors = meteors,
remaining_meteors = table.deepcopy(meteors),
defences_activated = 0,
point_defences_activated = 0,
defences = defences,
skip = 60
}
table.insert(global.meteor_showers, meteor_shower)
local surface = Zone.get_make_surface(zone)
--surface.request_to_generate_chunks(start_position)
--surface.request_to_generate_chunks(shadow_start_position)
--surface.request_to_generate_chunks(land_position)
for _, player in pairs(game.connected_players) do
if player.surface.index == surface.index then
player.play_sound{path = "se-meteor-woosh", volume = 3}
end
end
end
function Meteor.spawn_meteor_shower(meteor_shower)
local surface = Zone.get_make_surface(meteor_shower.zone)
for _, meteor in pairs(meteor_shower.remaining_meteors) do
surface.create_entity{
name = mod_prefix.."falling-meteor-"..string.format("%02d", math.random(Meteor.meteor_variants)),
position = meteor_shower.start_position,
target = meteor.land_position,
force = "neutral",
speed = Util.vectors_delta_length(meteor_shower.start_position, meteor.land_position) / (Meteor.meteor_fall_time + Meteor.meteor_chain_delay * meteor.id)
}
surface.create_entity{
name = mod_prefix.."shadow-meteor-"..string.format("%02d", math.random(Meteor.meteor_variants)),
position = meteor_shower.shadow_start_position,
target = meteor.land_position,
force = "neutral",
speed = Util.vectors_delta_length(meteor_shower.shadow_start_position, meteor.land_position) / (Meteor.meteor_fall_time + Meteor.meteor_chain_delay * meteor.id)
}
end
end
function Meteor.on_tick_defence (zone, defence)
if defence.container and defence.container.valid and defence.charger and defence.charger.valid then
if defence.charger.is_connected_to_electric_network() and defence.charger.energy < Meteor.defence_full_power * 0.99 then
if not defence.charging_shape_id then
defence.charging_shape_id = rendering.draw_sprite{
sprite = "utility/recharge_icon",
surface = defence.container.surface,
target = defence.container,
x_scale = 0.5,
y_scale = 0.5,
}
end
if defence.charging_text_id then
rendering.destroy(defence.charging_text_id)
defence.charging_text_id = nil
end
defence.charging_text_id = rendering.draw_text{
text = string.format("%.2f", 100 * defence.charger.energy / Meteor.defence_full_power) .. "%",
surface = defence.container.surface,
target = defence.container,
target_offset = {x = 0, y = 1},
color = {r=255,g=255,b=255,a=255},
alignment = "center",
}
else
if defence.charging_shape_id then
rendering.destroy(defence.charging_shape_id)
defence.charging_shape_id = nil
end
if defence.charging_text_id then
rendering.destroy(defence.charging_text_id)
defence.charging_text_id = nil
end
end
if defence.container.health <= 0 then defence.container.active = true end
else
if defence.container and defence.container.valid then
-- first search for an alternate charger at the same location (cant' do this with a migration :(
local charger = defence.container.surface.find_entity(Meteor.name_meteor_defence_charger, defence.container.position)
if charger and charger.valid then
defence.charger=charger
zone.meteor_defences[defence.unit_number]=defence
Meteor.on_tick_defence (zone, defence)
return
end
--game.print("destroying_container - didn't find a charger")
defence.container.destroy() end
if defence.charger and defence.charger.valid then
defence.charger.destroy()
end
if defence.charging_shape_id then
rendering.destroy(defence.charging_shape_id)
defence.charging_shape_id = nil
end
if defence.charging_text_id then
rendering.destroy(defence.charging_text_id)
defence.charging_text_id = nil
end
zone.meteor_defences[defence.unit_number] = nil
end
end
function Meteor.on_tick_point_defence (zone, defence)
if defence.container and defence.container.valid and defence.charger and defence.charger.valid then
if defence.charger.is_connected_to_electric_network() and defence.charger.energy < Meteor.point_defence_fire_power then
if not defence.charging_shape_id then
defence.charging_shape_id = rendering.draw_sprite{
sprite = "utility/recharge_icon",
surface = defence.container.surface,
target = defence.container,
x_scale = 0.5,
y_scale = 0.5,
}
end
if defence.charging_text_id then
rendering.destroy(defence.charging_text_id)
defence.charging_text_id = nil
end
defence.charging_text_id = rendering.draw_text{
text = string.format("%.2f", 100 * defence.charger.energy / (Meteor.point_defence_fire_power)) .. "%",
surface = defence.container.surface,
target = defence.container,
target_offset = {x = 0, y = 1},
color = {r=255,g=255,b=255,a=255},
alignment = "center",
}
else
if defence.charging_shape_id then
rendering.destroy(defence.charging_shape_id)
defence.charging_shape_id = nil
end
if defence.charging_text_id then
rendering.destroy(defence.charging_text_id)
defence.charging_text_id = nil
end
end
if defence.container.health <= 0 then defence.container.active = true end
else
if defence.container and defence.container.valid then
-- first search for an alternate charger at the same location (cant' do this with a migration :(
local charger = defence.container.surface.find_entity(Meteor.name_meteor_point_defence_charger, defence.container.position)
if charger and charger.valid then
defence.charger=charger
charger.destructible = false
zone.meteor_point_defences[defence.unit_number]=defence
--game.print("fixed_charger-normal")
Meteor.on_tick_point_defence (zone, defence)
return
end
charger = defence.container.surface.find_entity(Meteor.name_meteor_point_defence_charger.."-overcharged", defence.container.position)
if charger and charger.valid then defence.charger=charger
charger.destructible = false
zone.meteor_point_defences[defence.unit_number]=defence
--game.print("fixed_charger-overcharged")
Meteor.on_tick_point_defence (zone, defence)
return
end
--game.print("destroying_container,beacuse no charger")
defence.container.destroy() end
if defence.charger and defence.charger.valid then defence.charger.destroy() end
if defence.charging_shape_id then
rendering.destroy(defence.charging_shape_id)
defence.charging_shape_id = nil
end
if defence.charging_text_id then
rendering.destroy(defence.charging_text_id)
defence.charging_text_id = nil
end
zone.meteor_point_defences[defence.unit_number] = nil
end
end
function Meteor.credit_kill(charger, killed_entity_name)
--Credit the force with the kill
--All of the icons look the same so there's no reason in spltting the stastics up.
--force.kill_count_statistics.on_flow(mod_prefix .. "static-meteor-" .. string.format("%02d", math.random(Meteor.meteor_variants)), 1)
charger.force.kill_count_statistics.on_flow(mod_prefix .. "static-meteor-01", 1)
charger.kills = charger.kills + 1
end
function Meteor.on_tick (event)
-- TODO: make meteors appear on zones with power poles, not just where players are.
if game.tick % Meteor.tick_skip == 0 then
-- process meteor showers like tick tasks
if global.meteor_showers then
for i = #global.meteor_showers, 1, -1 do
if global.meteor_showers[i].valid then
Meteor.tick_meteor_shower(global.meteor_showers[i])
else
table.remove(global.meteor_showers, i)
end
end
end
for _, zone in pairs(global.meteor_zones or {}) do
if zone.meteor_defences then
for _, defence in pairs(zone.meteor_defences) do
Meteor.on_tick_defence(zone, defence)
end
end
if zone.meteor_point_defences then
for _, defence in pairs(zone.meteor_point_defences) do
Meteor.on_tick_point_defence(zone, defence)
end
end
end
end
end
Event.addListener(defines.events.on_tick, Meteor.on_tick)
function Meteor.on_nth_tick_1800 (event)
if not global.meteor_zones then
global.meteor_zones = {}
local nauvis = Zone.from_name("Nauvis")
global.meteor_zones[nauvis.index] = nauvis
end
for _, zone in pairs(global.meteor_zones or {}) do
-- trigger meteor showers, should be after to let chunks generate
if game.tick > (zone.next_meteor_shower or 3600) then
local star = Zone.get_star_from_child(zone)
local s_gravity = 0 -- 0 in deep space, 1 at roughly 1/3 out from the star
if star then s_gravity = math.min( 1, (Zone.get_star_gravity_well(zone) / Zone.get_star_gravity_well(star)) * 1.5) end
local m_multiplier = 1 + (1 - s_gravity) * 3 -- outer space can be 4x longer between strikes
zone.next_meteor_shower = game.tick + 60*60 + m_multiplier * math.random() * 60 * 60 * settings.global["se-meteor-interval"].value
local surface = Zone.get_make_surface(zone)
local position
local entities
entities = surface.find_entities_filtered{type="character"}
if #entities > 0 then
position = entities[math.random(#entities)].position
end
if not position then
entities = surface.find_entities_filtered{type={"car", "spider-vehicle", "locomotive", "cargo-wagon"}}
if #entities > 0 then
position = entities[math.random(#entities)].position
end
end
if not position then
entities = surface.find_entities_filtered{type={"electric-pole"}}
if #entities > 0 then
position = entities[math.random(#entities)].position
end
end
if not position then
position = {x = 0, y = 0}
end
Meteor.begin_meteor_shower(zone, position)
end
end
end
Event.addListener("on_nth_tick_1800", Meteor.on_nth_tick_1800) -- 30s
function on_runtime_mod_setting_changed()
for _, zone in pairs(global.meteor_zones or {}) do
if (zone.next_meteor_shower or 0) > game.tick + 60 * 60 * settings.global["se-meteor-interval"].value then
local star = Zone.get_star_from_child(zone)
local s_gravity = 0 -- 0 in deep space, 1 at roughly 1/3 out from the star
if star then s_gravity = math.min( 1, (Zone.get_star_gravity_well(zone) / Zone.get_star_gravity_well(star)) * 1.5) end
local m_multiplier = 1 + (1 - s_gravity) * 3 -- outer space can be 4x longer between strikes
zone.next_meteor_shower = game.tick + 30*60 + m_multiplier * math.random() * 60 * 60 * settings.global["se-meteor-interval"].value
end
end
end
Event.addListener(defines.events.on_runtime_mod_setting_changed, on_runtime_mod_setting_changed)
function Meteor.on_mode_toggle(event)
local player = game.players[event.player_index]
if not player.character then return end
local entity = player.selected
if entity and entity.valid then
--player.print("on_player_rotates: " .. entity.name .. " : " .. entity.type)
if entity.name == Meteor.name_meteor_point_defence_container then
local zone = Zone.from_surface(entity.surface)
local defence = zone.meteor_point_defences[entity.unit_number]
if not defence then return end -- probably on spaceship
local charger = defence.charger
if charger then
if defence.charging_text_id then
rendering.destroy(defence.charging_text_id)
defence.charging_text_id = nil
end
if defence.charging_shape_id then
rendering.destroy(defence.charging_shape_id)
defence.charging_shape_id = nil
end
local chargerType
if charger.name == Meteor.name_meteor_point_defence_charger_overcharged then
chargerType = Meteor.name_meteor_point_defence_charger
player.print({"space-exploration.charge-mode-normal"})
if defence.extra_overlay_id and rendering.is_valid(defence.extra_overlay_id) then
rendering.destroy(defence.extra_overlay_id)
end
defence.extra_overlay_id = nil
else
chargerType = Meteor.name_meteor_point_defence_charger_overcharged
player.print({"space-exploration.charge-mode-fast"})
if defence.extra_overlay_id and rendering.is_valid(defence.extra_overlay_id) then
rendering.destroy(defence.extra_overlay_id)
end
defence.extra_overlay_id = rendering.draw_sprite{
sprite = Meteor.name_meteor_point_defence_mask,
surface = defence.container.surface,
target = defence.container,
tint = {r=255,g=128,b=0},
render_layer = "lower-object-above-shadow"
}
end
charger.destroy()
charger = entity.surface.create_entity{
name = chargerType,
position = entity.position,
direction = entity.direction,
force = entity.force
}
charger.destructible = false
charger.energy = 0
if not zone.meteor_point_defences then
zone.meteor_point_defences = {}
end
zone.meteor_point_defences[entity.unit_number] = {
unit_number = entity.unit_number,
container = entity,
charger = charger,
extra_overlay_id = defence.extra_overlay_id
}
else
player.print("No "..Meteor.name_meteor_point_defence_charger .." found")
end
end
end
end
Event.addListener(mod_prefix .. 'mode-toggle', Meteor.on_mode_toggle)
return Meteor