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.

747 lines
33 KiB

local SpaceshipObstacles = {}
SpaceshipObstacles.particle_enemy_proxy = mod_prefix .. "spaceship-enemy-proxy"
SpaceshipObstacles.particle_travel_anchor = mod_prefix .. "spaceship-travel-anchor"
SpaceshipObstacles.particle_hp_multiplier = 300
SpaceshipObstacles.bounding_box_buffer = 2
-- This dictates how much sideways velocity the asteroids get
-- (0.00 is purely vertical)
--SpaceshipObstacles.particle_orientation_variance = 0.00
SpaceshipObstacles.particle_orientation_variance = 0.03
-- sprite_prefix: append variation index, used by rendering api
-- vehicle_name: the actual vehicle being used to move the asteroid
-- Speed values are not true speed, asteroid areas have a multiplier.
SpaceshipObstacles.particles = {
["speck"] = { multiplier = 1/1000, speed = 1/100, min_speed = 0, max_effective_speed = 50,
sprite_name = mod_prefix.."spaceship-speck-graphic"
},
["small"] = {
multiplier = 1/8000, speed = 1/400, min_speed = 5,
variations = 16, scale = 0.25, max_effective_speed = 100,
sprite_prefix = mod_prefix .. "spaceship-obstacle-small-sprite-",
vehicle_name = mod_prefix .. "spaceship-obstacle-small-vehicle",
},
["medium"] = {
multiplier = 1/60000, speed = 1/450, min_speed = 25, destroys_floor = true,
variations = 16, scale = 0.5, max_effective_speed = 200,
sprite_prefix = mod_prefix .. "spaceship-obstacle-medium-sprite-",
vehicle_name = mod_prefix .. "spaceship-obstacle-medium-vehicle",
},
["large"] = {
multiplier = 1/3000000, speed = 1/500, min_speed = 100, min_size = 1000, destroys_floor = true,
variations = 16, scale = 1,
sprite_prefix = mod_prefix .. "spaceship-obstacle-large-sprite-",
vehicle_name = mod_prefix .. "spaceship-obstacle-large-vehicle",
},
}
SpaceshipObstacles.entity_particle_templates = {
["ice"] = {speed = 1/300},
["small"] = {
speed = 1/300,
projectile_name = mod_prefix.."spaceship-obstacle-entity-small-projectile",
targetable_name = mod_prefix.."spaceship-obstacle-entity-small-targetable",
},
["large"] = {
speed = 1/600,
projectile_name = mod_prefix.."spaceship-obstacle-entity-large-projectile",
targetable_name = mod_prefix.."spaceship-obstacle-entity-large-targetable",
}
}
-- names of the entities used to make the asteroids mineable when a ship is stopped
SpaceshipObstacles.mineable_particles = {}
for x=1,16 do
table.insert(SpaceshipObstacles.mineable_particles, mod_prefix .. "spaceship-obstacle-small-static-"..x)
table.insert(SpaceshipObstacles.mineable_particles, mod_prefix .. "spaceship-obstacle-medium-static-"..x)
table.insert(SpaceshipObstacles.mineable_particles, mod_prefix .. "spaceship-obstacle-large-static-"..x)
end
SpaceshipObstacles.collision_slowdown_factor = 0.98 -- slow down the ship when an obstacle collides with it
SpaceshipObstacles.particle_spawn_range = 32 -- same as laser turret prepare range
SpaceshipObstacles.all_targetables = {}
for _, particle in pairs(SpaceshipObstacles.particles) do
if particle.vehicle_name then
table.insert(SpaceshipObstacles.all_targetables, particle.vehicle_name)
end
end
SpaceshipObstacles.obstacle_damage_multipliers = {
["electric-turret"] = 120,
["ammo-turret"] = 12
}
SpaceshipObstacles.default_asteroid_density = 1
SpaceshipObstacles.asteroid_density_by_zone_type = {
["asteroid-belt"] = 2,
["asteroid-field"] = 3
}
SpaceshipObstacles.near_enough_by_zone_type = {
["asteroid-belt"] = 300,
["asteroid-field"] = 4500
}
function SpaceshipObstacles.particle_speed_factor(spaceship_speed) -- particle speed from spaceship speed
return spaceship_speed > 0 and math.pow(spaceship_speed / Spaceship.speed_taper, Spaceship.particle_speed_power) * Spaceship.speed_taper or 0
end
--- Gets the asteroid density caption for a spaceship
---@param spaceship Spaceship the spaceship data
function SpaceshipObstacles.get_asteroid_density_caption(spaceship)
if not spaceship.asteroid_density then return {"space-exploration.spaceship-asteroid-density-default"} end
if spaceship.asteroid_density < SpaceshipObstacles.asteroid_density_by_zone_type["asteroid-belt"] then return {"space-exploration.spaceship-asteroid-density-default"} end
if spaceship.asteroid_density < SpaceshipObstacles.asteroid_density_by_zone_type["asteroid-field"] then return {"space-exploration.spaceship-asteroid-density-belt"} end
return {"space-exploration.spaceship-asteroid-density-field"}
end
--- Gets the asteroid density for a spaceship
---@param spaceship Spaceship the spaceship data
function SpaceshipObstacles.get_asteroid_density(spaceship)
local density = SpaceshipObstacles.default_asteroid_density
local zone = Zone.find_nearest_zone(
spaceship.space_distortion,
spaceship.stellar_position,
spaceship.star_gravity_well,
spaceship.planet_gravity_well
)
local delta_v = Zone.get_travel_delta_v_sub(spaceship, zone)
if zone and zone.type and SpaceshipObstacles.asteroid_density_by_zone_type[zone.type] and delta_v < SpaceshipObstacles.near_enough_by_zone_type[zone.type] then
density = SpaceshipObstacles.asteroid_density_by_zone_type[zone.type]
end
return density
end
--[[
Destroys all particles on a surface
]]
function SpaceshipObstacles.destroy(spaceship, surface)
if spaceship.particle_object_ids then
for _, ids in pairs(spaceship.particle_object_ids) do
for _, id in pairs(ids) do
rendering.destroy(id)
end
end
spaceship.particle_object_ids = nil
end
local destroy_names = {}
table.insert(destroy_names, SpaceshipObstacles.particle_travel_anchor)
for _, particle in pairs(SpaceshipObstacles.particles) do
if particle.vehicle_name then
table.insert(destroy_names, particle.vehicle_name)
end
end
for _, mineable_particle in pairs(SpaceshipObstacles.mineable_particles) do
table.insert(destroy_names, mineable_particle)
end
local destroy_entities = surface.find_entities_filtered{name = destroy_names}
for _, entity in pairs(destroy_entities) do
-- this does not raise any event!
-- it will silently destroy the entity
-- if any mod was tracking the space rocks, it will get borked by this
-- why do potentially other-mod breaking thing?
-- we do this for performance reasons because raising events is expensive
entity.destroy()
end
for _, proxy in pairs(surface.find_entities_filtered{name = SpaceshipObstacles.particle_enemy_proxy}) do
util.safe_destroy(proxy)
end
end
--[[
Travel anchor particles are used for visual only particles that move exactly downward.
One particle may have many sprites attached to it.
As such, travel anchor particles are reused based on speed and position along the y-axis.
This will get the travel anchor particle for a speed and position, or create it if it does not exist.
]]
function SpaceshipObstacles.get_make_travel_anchor_particle(spaceship, surface, y, particle_template)
local speed = particle_template.speed
if not (spaceship.travel_anchor_particles
and spaceship.travel_anchor_particles[speed]
and spaceship.travel_anchor_particles[speed].valid
and y + 10 > spaceship.travel_anchor_particles[speed].position.y) then
spaceship.travel_anchor_particles = spaceship.travel_anchor_particles or {}
spaceship.travel_anchor_particles[speed] = surface.create_entity({
name = SpaceshipObstacles.particle_travel_anchor,
position = {
x = spaceship.known_bounds.left_top.x - SpaceshipObstacles.particle_spawn_range,
y = spaceship.known_bounds.left_top.y - SpaceshipObstacles.particle_spawn_range - 10 - 10
},
direction = defines.direction.south
})
table.insert(spaceship.particles, {
valid = true,
vehicle = spaceship.travel_anchor_particles[speed],
speed = speed,
})
end
return spaceship.travel_anchor_particles[speed]
end
--[[
Computes the number of asteroids to spawn for a certain template.
The number of asteroids to spawn is based on the spaceship's speed and the time passed.
]]
function SpaceshipObstacles.get_num_particles_to_spawn(spaceship, time_passed, particle_template)
local density = spaceship.asteroid_density or SpaceshipObstacles.default_asteroid_density
local effective_speed = spaceship.speed * density
if particle_template.max_effective_speed then
effective_speed = math.min(effective_speed, particle_template.max_effective_speed)
end
local spawn_particles_quota = (Spaceship.minimum_mass + spaceship.integrity_stress) * effective_speed / 60 * particle_template.multiplier * time_passed
local spawn_particles_whole = math.floor(spawn_particles_quota)
-- we can only ever spawn a whole number of particles so use math.random() to average out the decimal part of spawn_particles_quota
local spawn_particles_chance = spawn_particles_quota - spawn_particles_whole
if math.random() <= spawn_particles_chance then
spawn_particles_whole = spawn_particles_whole + 1
end
return spawn_particles_whole
end
--[[
Determines if a surface has no players on it. Useful for optimizing away visual particles if no one can see them.
]]
function SpaceshipObstacles.surface_has_no_players(surface)
local no_players = true
for _, player in pairs(game.players) do
if player.valid and player.surface.index == surface.index then
no_players = false
end
end
return no_players
end
--[[
Spawns a visual only particle.
These particles are always just sprites attached to a travel anchor particle by LuaRendering.draw_sprite
]]
function SpaceshipObstacles.spawn_particles_visual_only(spaceship, surface, time_passed, particle_template)
local spawn_particles_whole = SpaceshipObstacles.get_num_particles_to_spawn(spaceship, time_passed, particle_template)
if spawn_particles_whole > 0 then
local speed_factor = SpaceshipObstacles.particle_speed_factor(spaceship.speed)
local x_min = spaceship.known_bounds.left_top.x - SpaceshipObstacles.particle_spawn_range
local x_max = spaceship.known_bounds.right_bottom.x + SpaceshipObstacles.particle_spawn_range
local spawn_y = spaceship.known_bounds.left_top.y - SpaceshipObstacles.particle_spawn_range - 20
local travel_anchor_particle = SpaceshipObstacles.get_make_travel_anchor_particle(spaceship, surface, spawn_y, particle_template)
spaceship.particle_object_ids = spaceship.particle_object_ids or {}
spaceship.particle_object_ids[travel_anchor_particle.unit_number] = spaceship.particle_object_ids[travel_anchor_particle.unit_number] or {}
travel_anchor_particle.speed = particle_template.speed * spaceship.speed
local x_delta = x_max - x_min
local y_factor = time_passed * speed_factor * particle_template.speed
for i = 1, spawn_particles_whole do
local spawn_x = x_min + math.random() * x_delta
local this_spawn_y = spawn_y - math.random() * y_factor
local object_id = rendering.draw_sprite({
surface = surface,
sprite = particle_template.sprite_name,
target = travel_anchor_particle,
target_offset = {
x = spawn_x - travel_anchor_particle.position.x,
y = this_spawn_y - travel_anchor_particle.position.y
}
})
table.insert(spaceship.particle_object_ids[travel_anchor_particle.unit_number], object_id)
end
end
end
--[[
Spawns a non-visual only particle.
Asteroids are invisible vehicles, with an invisible enemy character riding them that have a graphic attached with LuaRendering.draw_sprite
]]
function SpaceshipObstacles.spawn_particles_non_visual(spaceship, surface, time_passed, particle_template, no_players)
local spawn_particles_whole = SpaceshipObstacles.get_num_particles_to_spawn(spaceship, time_passed, particle_template)
if spawn_particles_whole > 0 then
local speed_factor = SpaceshipObstacles.particle_speed_factor(spaceship.speed)
local x_min = spaceship.known_bounds.left_top.x - SpaceshipObstacles.particle_spawn_range
local x_max = spaceship.known_bounds.right_bottom.x + SpaceshipObstacles.particle_spawn_range
local spawn_y = spaceship.known_bounds.left_top.y - SpaceshipObstacles.particle_spawn_range - 20
-- aabb cannot be exactly the ship bounds since asteroids are not points (they have a rectangular bounding box)
local aabb = {
min_x = spaceship.known_bounds.left_top.x - SpaceshipObstacles.bounding_box_buffer,
max_x = spaceship.known_bounds.right_bottom.x + SpaceshipObstacles.bounding_box_buffer,
min_y = spaceship.known_bounds.left_top.y - SpaceshipObstacles.bounding_box_buffer,
max_y = spaceship.known_bounds.right_bottom.y + SpaceshipObstacles.bounding_box_buffer
}
local speed = particle_template.speed * spaceship.speed
local x_delta = x_max - x_min
local y_factor = time_passed * speed_factor * particle_template.speed
for i = 1, spawn_particles_whole do
local spawn_x = x_min + math.random() * x_delta
local this_spawn_y = spawn_y - math.random() * y_factor
local spawn_orientation = 0.5 - SpaceshipObstacles.particle_orientation_variance * (math.random() - math.random())
local intersects = util.intersects_ray_aabb({x=spawn_x,y=spawn_y}, spawn_orientation, aabb)
if not no_players or intersects then
local particle = table.deepcopy(particle_template)
particle.valid = true
particle.variation = math.random(particle.variations)
particle.vehicle = surface.create_entity{
name = particle_template.vehicle_name,
position = {x = spawn_x, y = this_spawn_y },
create_build_effect_smoke = false,
}
particle.vehicle.orientation = spawn_orientation
-- smart turrets only attack asteroids that will hit the ship
if intersects then
particle.enemy_proxy = surface.create_entity{
name = SpaceshipObstacles.particle_enemy_proxy,
position = {x = spawn_x, y = this_spawn_y },
}
particle.vehicle.set_driver(particle.enemy_proxy)
end
rendering.draw_sprite{
sprite = particle.sprite_prefix..particle.variation,
surface = surface,
target = particle.vehicle,
scale = particle.scale
}
particle.vehicle.speed = speed
table.insert(spaceship.particles, particle)
end
end
end
end
--[[
Spawns particles from a particle_template.
]]
function SpaceshipObstacles.spawn_particles_speed_size(spaceship, surface, time_passed, particle_template)
-- only spawn the asteroids if the conditions on their template are met
if spaceship.speed > (particle_template.min_speed or 0)
and spaceship.integrity_stress > (particle_template.min_size or 0) then
local no_players = SpaceshipObstacles.surface_has_no_players(surface)
if particle_template.sprite_name then
-- visual particles are only spawned if players are on that surface
if not no_players then
SpaceshipObstacles.spawn_particles_visual_only(spaceship, surface, time_passed, particle_template)
end
else
SpaceshipObstacles.spawn_particles_non_visual(spaceship, surface, time_passed, particle_template, no_players)
end
end
end
--[[
Resolves a collision between a particle and an obstacle it collided with.
Either the particle or the obstacle will survive, not both.
If the particle has enough effective health to kill the obstacle, the obstacle dies and the particle loses hp
Otherwise, the obstacle has enough effective health to kill the particle, so the particle dies and the obstacle loses hp
]]
function SpaceshipObstacles.smash_particle_to_entity(particle_entity, other_entity)
local particle_effective_health = particle_entity.health / SpaceshipObstacles.particle_hp_multiplier
if particle_effective_health > other_entity.health then
-- the particle "wins" the collision
local other_effective_health = other_entity.health * SpaceshipObstacles.particle_hp_multiplier
particle_entity.health = particle_entity.health - other_effective_health
other_entity.die()
else
-- the obstacle "wins" the collision
other_entity.health = other_entity.health - particle_effective_health
if particle_entity.type == "car" and particle_entity.get_driver() then
particle_entity.get_driver().destroy()
end
particle_entity.die()
end
end
--[[
Listen to the event when an entity is damaged. Necessary in order to modify the damage done to/from particles
]]
function SpaceshipObstacles.on_entity_damaged(event)
--[[
In a vehicle collision:
the struck object takes damage first with the vehicle as the cause.
Then the striking vehile takes damage with no cause. (may be the crash trigger damage?)
]]
if event.entity and event.entity.valid then
if Util.table_contains(SpaceshipObstacles.all_targetables, event.entity.name) then
if event.cause and event.cause.valid then
if SpaceshipObstacles.obstacle_damage_multipliers[event.cause.type] then -- take more damage from certain sources.
event.entity.health = event.entity.health - event.final_damage_amount * (SpaceshipObstacles.obstacle_damage_multipliers[event.cause.type] - 1)
end
end
event.entity.surface.create_trivial_smoke{name = "smoke", position = event.entity.position }
elseif event.cause and event.cause.valid and Util.table_contains(SpaceshipObstacles.all_targetables, event.cause.name) then
if event.damage_type.name == "impact" then
if event.entity.name ~= "shield-projector-barrier" then
local spaceship = Spaceship.from_own_surface_index(event.entity.surface.index)
if spaceship then spaceship.speed = spaceship.speed * SpaceshipObstacles.collision_slowdown_factor end
end
SpaceshipObstacles.smash_particle_to_entity(event.cause, event.entity)
if event.cause.valid then
global.spaceship_projectile_speeds = global.spaceship_projectile_speeds or {}
table.insert(global.spaceship_projectile_speeds, {entity = event.cause, speed = event.cause.speed})
end
end
end
end
end
Event.addListener(defines.events.on_entity_damaged, SpaceshipObstacles.on_entity_damaged)
function SpaceshipObstacles.tick_projectile_speeds()
if global.spaceship_projectile_speeds then
for _, ps in pairs(global.spaceship_projectile_speeds) do
if ps.entity and ps.entity.valid and ps.entity.type == "car" then
ps.entity.speed = ps.speed
end
end
global.spaceship_projectile_speeds = nil
end
end
--[[
Listen for when vehicles are destroyed so we can destroy their dummy drivers.
]]
function SpaceshipObstacles.on_removed_entity(event)
if event.entity and event.entity.valid then
if event.entity.type == "car" and Util.table_contains(SpaceshipObstacles.all_targetables, event.entity.name) then
local driver = event.entity.get_driver()
if driver then driver.destroy() end
end
end
end
Event.addListener(defines.events.on_entity_died, SpaceshipObstacles.on_removed_entity)
Event.addListener(defines.events.on_robot_mined_entity, SpaceshipObstacles.on_removed_entity)
Event.addListener(defines.events.on_player_mined_entity, SpaceshipObstacles.on_removed_entity)
Event.addListener(defines.events.script_raised_destroy, SpaceshipObstacles.on_removed_entity)
--- Adds an entity to the pool of moveable entities.
--- Moveable entities act like obstacles in that:
--- 1. they move downwards
--- 2. they are targetted by turrets
--- 3. they get destroyed by shields
--- They are dissimilar in that:
--- 1. they don't damage anything on the ship
--- 2. they suck for UPS
---@param spaceship any
---@param entity any
function SpaceshipObstacles.add_moveable_entity(spaceship, entity, name)
if spaceship and entity then
spaceship.entity_particles = spaceship.entity_particles or {}
local entity_particle = table.deepcopy(SpaceshipObstacles.entity_particle_templates[name])
entity_particle.graphic = entity
entity_particle.valid = true
table.insert(spaceship.entity_particles, entity_particle)
end
end
--- Debris needs to be moved. This trigger listens for when that is created on a spaceship surface,
--- and if so, puts it in the list of things that need to move.
---@param event any
function SpaceshipObstacles.on_trigger_created_entity(event)
if event.entity and event.entity.valid and event.entity.name == mod_prefix.."trigger-movable-debris" then
local surface = event.entity.surface
local spaceship = Spaceship.from_own_surface_index(surface.index)
if spaceship then
local entities = surface.find_entities_filtered{type = "simple-entity", area = Util.position_to_area(event.entity.position, 0.5)}
for _, entity in pairs(entities) do
if string.find(entity.name, "meteor") then
SpaceshipObstacles.add_moveable_entity(spaceship, entity, "large")
else
SpaceshipObstacles.add_moveable_entity(spaceship, entity, "small")
end
end
end
end
end
Event.addListener(defines.events.on_trigger_created_entity, SpaceshipObstacles.on_trigger_created_entity)
--- Moves the moveable entities. Handles destroying them safely if one of the component entities is destroyed or if they get offscreen.
---@param spaceship Spaceship spaceship data
---@param surface LuaSurface spaceship surface
function SpaceshipObstacles.tick_entity_obstacles(spaceship, surface)
spaceship.entity_particles = spaceship.entity_particles or {}
local speed_factor = SpaceshipObstacles.particle_speed_factor(spaceship.speed)
local upper_bound = spaceship.known_bounds.right_bottom.y + SpaceshipObstacles.particle_spawn_range + 32
for _, entity_particle in pairs(spaceship.entity_particles) do
if not (entity_particle.graphic and entity_particle.graphic.valid) then -- killed or mined
entity_particle.valid = false
elseif entity_particle.graphic.position.y > upper_bound then -- out of range
entity_particle.valid = false
elseif entity_particle.projectile and entity_particle.projectile.valid == false then -- projectile was there but detonated
entity_particle.valid = false
elseif entity_particle.targetable and entity_particle.targetable.valid == false then -- projectile detonated
entity_particle.valid = false
end
if not entity_particle.valid then
-- destroy invalid entity particles
if entity_particle.graphic and entity_particle.graphic.valid then
entity_particle.graphic.destroy()
entity_particle.graphic = nil
end
if entity_particle.projectile and entity_particle.projectile.valid then
entity_particle.projectile.destroy()
entity_particle.projectile = nil
end
if entity_particle.targetable and entity_particle.targetable.valid then
entity_particle.targetable.destroy()
entity_particle.targetable = nil
end
spaceship.entity_particles[_] = nil
else
-- update the speed and move valid entity particles
local position = entity_particle.graphic.position
position.y = position.y + entity_particle.speed * speed_factor
if spaceship.speed > 0.001 then
entity_particle.graphic.teleport(position)
if entity_particle.projectile then
entity_particle.projectile.speed = entity_particle.speed * speed_factor
elseif entity_particle.projectile_name then
entity_particle.projectile = surface.create_entity{
name = entity_particle.projectile_name,
-- particle would overtake the rest if we didn't subtract the current vector
position = util.vectors_add(position, {x = 0, y = -entity_particle.speed * speed_factor}),
speed = entity_particle.speed * speed_factor,
target = {x = position.x, y = position.y + 100},
force="enemy"
}
end
if entity_particle.targetable then
entity_particle.targetable.teleport(position)
elseif entity_particle.targetable_name then
entity_particle.targetable = surface.create_entity{
name = entity_particle.targetable_name,
position = position,
target = {x = position.x, y = position.y + 100},
force="enemy"
}
end
else
if entity_particle.projectile and entity_particle.projectile.valid then
entity_particle.projectile.destroy()
entity_particle.projectile = nil
end
if entity_particle.targetable and entity_particle.targetable.valid then
entity_particle.targetable.destroy()
entity_particle.targetable = nil
end
end
end
end
end
function SpaceshipObstacles.tick_obstacles(spaceship, surface, time_passed)
spaceship.particles = spaceship.particles or {}
spaceship.mineables = spaceship.mineables or {}
-- spawn particles of each template
for _, particle_template in pairs(SpaceshipObstacles.particles) do
SpaceshipObstacles.spawn_particles_speed_size(spaceship, surface, time_passed, particle_template)
end
-- update particles
if spaceship.particles then
local speed_factor = SpaceshipObstacles.particle_speed_factor(spaceship.speed)
local upper_bound = spaceship.known_bounds.right_bottom.y + SpaceshipObstacles.particle_spawn_range + 32
for _, particle in pairs(spaceship.particles) do
if not (particle.vehicle and particle.vehicle.valid
and particle.vehicle.position.y < upper_bound) then -- the particle is in bounds
particle.valid = false
end
if not particle.valid then
if particle.vehicle and particle.vehicle.valid then
particle.vehicle.destroy()
particle.vehicle = nil
end
if particle.enemy_proxy and particle.enemy_proxy.valid then
particle.enemy_proxy.destroy()
particle.enemy_proxy = nil
end
spaceship.particles[_] = nil
end
if particle and particle.valid then -- particle is still valid
particle.vehicle.speed = particle.speed * speed_factor
local targetable = particle.vehicle
local position = targetable.position
if targetable and particle.destroys_floor then
-- destroy floor
local x = math.floor(position.x)
local y = math.floor(position.y)
if spaceship.known_tiles[x] and spaceship.known_tiles[x][y] then
spaceship.known_tiles[x][y] = nil
local tile = surface.get_tile(x, y)
local tile_name = tile.name
if tile_name ~= name_space_tile then
targetable.damage(50, "neutral", "explosion") -- damage the particle
local entities = surface.find_entities_filtered{ -- destroy entities on the floor
area = util.tile_to_area({x=x,y=y}),
collision_mask = {
global.named_collision_masks.space_collision_layer,
"object-layer",
"resource-layer"
}
}
for _, entity in pairs(entities) do
if entity and entity.valid then entity.die() end
end
surface.set_tiles(
{{name=name_space_tile, position = {x,y}}},
false, -- corect tiles
true, -- remove_colliding_entities
true, -- remove_colliding_decoratives
true -- raise_event
)
if Util.table_contains(Spaceship.names_spaceship_floors, tile_name) then
-- make blueprint for the tile.
surface.create_entity{name = "tile-ghost", inner_name = tile_name, force = spaceship.force_name, position = {x=x,y=y}}
end
Spaceship.start_integrity_check(spaceship)
end
end
end
end
end
end
-- swap vehicles with mineables and vice-versa when the spaceship changes between stopped and moving
SpaceshipObstacles.maybe_swap_vehicles_mineables(spaceship, surface)
end
--[[
Converts name + variation from vehicle asteroids to mineable asteroids
]]
function SpaceshipObstacles.static_name_for_vehicle_name_and_variation(vehicle_name, variation)
if vehicle_name == mod_prefix .. "spaceship-obstacle-small-vehicle" then
return mod_prefix .. "spaceship-obstacle-small-static-"..variation
elseif vehicle_name == mod_prefix .. "spaceship-obstacle-medium-vehicle" then
return mod_prefix .. "spaceship-obstacle-medium-static-"..variation
elseif vehicle_name == mod_prefix .. "spaceship-obstacle-large-vehicle" then
return mod_prefix .. "spaceship-obstacle-large-static-"..variation
else
return nil
end
end
--[[
Converts name + variation from mineable asteroids to vehicle asteroids
]]
function SpaceshipObstacles.particle_template_for_static_name(static_name, variation)
if string.find(static_name,"spaceship-obstacle-small-static-", 1, true) then
return SpaceshipObstacles.particles["small"], variation
elseif string.find(static_name,"spaceship-obstacle-medium-static-", 1, true) then
return SpaceshipObstacles.particles["medium"], variation
elseif string.find(static_name,"spaceship-obstacle-large-static-", 1, true) then
return SpaceshipObstacles.particles["large"], variation
else
return nil, 1
end
end
--[[
Tries to swap vehicles asteroids with mineable asteroids when the spaceship's speed goes from moving to stopped
Tries to swap mineable asteroids with vehicle asteroids when the spaceship's speed goes from stopped to moving
]]
function SpaceshipObstacles.maybe_swap_vehicles_mineables(spaceship, surface)
if spaceship.speed and spaceship.last_speed then
if spaceship.speed == 0 and spaceship.speed ~= spaceship.last_speed then
-- spaceship came to a stop, replace vehicles with mineables
for _, particle in pairs(spaceship.particles) do
if particle.valid and particle.vehicle.valid then
-- make the mineable
local mineable_name = SpaceshipObstacles.static_name_for_vehicle_name_and_variation(particle.vehicle.name, particle.variation)
if mineable_name then
local mineable = {
mineable = surface.create_entity{
name=mineable_name,
position=particle.vehicle.position,
force="enemy"
},
variation=particle.variation
}
table.insert(spaceship.mineables, mineable)
end
-- destroy the vehicle
if particle.vehicle and particle.vehicle.valid then
particle.vehicle.destroy()
particle.vehicle = nil
end
if particle.enemy_proxy and particle.enemy_proxy.valid then
particle.enemy_proxy.destroy()
particle.enemy_proxy = nil
end
spaceship.particles[_] = nil
end
end
elseif spaceship.last_speed == 0 and spaceship.speed ~= spaceship.last_speed then
-- spaceship is starting moving, replaces mineables with vehicles
local aabb = {
min_x = spaceship.known_bounds.left_top.x,
max_x = spaceship.known_bounds.right_bottom.x,
min_y = spaceship.known_bounds.left_top.y,
max_y = spaceship.known_bounds.right_bottom.y
}
local no_players = true
for _, player in pairs(game.players) do
if player.valid and player.surface.index == surface.index then
no_players = false
end
end
for _, mineable in pairs(spaceship.mineables) do
if mineable.mineable and mineable.mineable.valid then
-- make the vehicle
local particle_template, variation = SpaceshipObstacles.particle_template_for_static_name(mineable.mineable.name, mineable.variation)
if particle_template then
local speed = particle_template.speed * spaceship.speed
local spawn_orientation = 0.5 - SpaceshipObstacles.particle_orientation_variance * (math.random() - math.random())
local intersects = util.intersects_ray_aabb({x=mineable.mineable.position.x,y=mineable.mineable.position.y}, spawn_orientation, aabb)
if not no_players or intersects then
local particle = table.deepcopy(particle_template)
particle.valid = true
particle.variation = variation
particle.vehicle = surface.create_entity{
name = particle_template.vehicle_name,
position = {x = mineable.mineable.position.x, y = mineable.mineable.position.y },
create_build_effect_smoke = false,
}
particle.vehicle.orientation = spawn_orientation
-- smart turrets only attack asteroids that will hit the ship
if intersects then
particle.enemy_proxy = surface.create_entity{
name = SpaceshipObstacles.particle_enemy_proxy,
position = {x = mineable.mineable.position.x, y = mineable.mineable.position.y },
}
particle.vehicle.set_driver(particle.enemy_proxy)
end
rendering.draw_sprite{
sprite = particle.sprite_prefix..particle.variation,
surface = surface,
target = particle.vehicle,
scale = particle.scale
}
particle.vehicle.speed = speed
table.insert(spaceship.particles, particle)
end
end
-- destroy the mineable
if mineable.mineable and mineable.mineable.valid then
mineable.mineable.destroy()
end
spaceship.mineables[_] = nil
end
end
end
end
spaceship.last_speed = spaceship.speed
end
return SpaceshipObstacles