2022-07-10 19:41:43 +02:00
local Event = require ' utils.event '
local Global = require ' utils.global '
2022-07-10 20:27:18 +02:00
local Gui = require ' utils.gui '
2022-07-10 19:41:43 +02:00
local this = {
2022-12-23 00:52:12 +01:00
renders = { } ,
valid_targets = {
' character ' ,
' tank ' ,
' car ' ,
' locomotive ' ,
' cargo-wagon ' ,
' fluid-wagon ' ,
' artillery-wagon ' ,
' artillery-turret ' ,
' spidertron '
2023-09-23 23:00:51 +02:00
} ,
backup_valid_targets = {
' character ' ,
' tank ' ,
' car ' ,
' locomotive ' ,
' cargo-wagon ' ,
' fluid-wagon ' ,
' artillery-wagon ' ,
' artillery-turret ' ,
' spidertron '
2022-12-23 00:52:12 +01:00
}
2022-07-10 19:41:43 +02:00
}
2022-07-10 20:27:18 +02:00
local Public = { }
Public.metatable = { __index = Public }
2022-07-10 19:41:43 +02:00
Global.register (
this ,
function ( tbl )
this = tbl
2022-07-10 20:27:18 +02:00
for _ , render in pairs ( this.renders ) do
setmetatable ( render , Public.metatable )
end
2022-07-10 19:41:43 +02:00
end
)
local sqrt = math.sqrt
local random = math.random
local remove = table.remove
local speed = 0.06
--- Draws a new render.
2023-04-07 01:31:15 +02:00
---@return table|nil
2022-07-10 20:27:18 +02:00
function Public : new_render ( )
local surface = game.get_surface ( self.surface_id )
2023-04-07 01:31:15 +02:00
if not surface or not surface.valid then
return
end
2022-07-10 21:37:11 +02:00
self.render_id = rendering.draw_sprite { target = self.position , sprite = self.sprite , surface = surface }
2022-10-25 20:52:44 +02:00
return self
2022-07-10 19:41:43 +02:00
end
--- Sets a new target for a given render.
2023-04-07 01:31:15 +02:00
---@return table|nil, table|nil
2022-07-10 20:27:18 +02:00
function Public : new_target ( )
local surface = game.get_surface ( self.surface_id )
2023-04-07 01:31:15 +02:00
if not surface or not surface.valid then
return
end
2022-07-10 19:41:43 +02:00
local position
2023-09-23 23:00:51 +02:00
local entities = surface.find_entities_filtered { type = this.valid_targets , force = ' player ' }
2022-07-10 19:41:43 +02:00
if entities and # entities > 0 then
position = entities [ random ( # entities ) ] . position
end
local chunk = surface.get_random_chunk ( )
local random_position = { x = ( chunk.x + random ( ) ) * 32 , y = ( chunk.y + random ( ) ) * 32 }
return position , random_position
end
--- Subtracts the given positions
---@return table|integer
2022-07-10 20:27:18 +02:00
function Public : subtr ( )
if not self.position and self.target_position then
2022-07-10 19:41:43 +02:00
return 0
end
2022-07-10 20:27:18 +02:00
return { x = self.target_position . x - self.position . x , y = self.target_position . y - self.position . y }
2022-07-10 19:41:43 +02:00
end
--- Sets the render scale.
2022-07-10 20:27:18 +02:00
function Public : set_render_scalar_size ( )
2022-07-10 21:37:11 +02:00
if not self.render_id then
return self : validate ( )
2022-07-10 19:41:43 +02:00
end
2022-07-10 21:37:11 +02:00
rendering.set_y_scale ( self.render_id , 3.5 ) -- 1.5
rendering.set_x_scale ( self.render_id , 7 ) -- 2
2022-07-10 19:41:43 +02:00
rendering.set_color (
2022-07-10 21:37:11 +02:00
self.render_id ,
2022-07-10 19:41:43 +02:00
{
r = 1 ,
g = 0.7 ,
b = 0.7
}
)
end
2023-04-07 01:31:15 +02:00
2022-07-10 19:41:43 +02:00
--- Gets a random position.
---@return table
2022-07-10 20:27:18 +02:00
function Public : random_position ( )
return { x = self.position . x + ( random ( ) - 0.5 ) * 64 , y = self.position . y + ( random ( ) - 0.5 ) * 64 }
2022-07-10 19:41:43 +02:00
end
--- Changes the position of a render.
---@param max_abs number
---@param value boolean
---@return table|nil
2022-07-10 20:27:18 +02:00
function Public : change_position ( max_abs , value )
if not self.position or not self.target_position then
2022-07-10 19:41:43 +02:00
return
end
local scalar = 0.9
2022-07-10 20:27:18 +02:00
local subtr = self : subtr ( )
2022-07-10 19:41:43 +02:00
if value then
subtr.y = subtr.y / scalar
end
2022-07-10 20:27:18 +02:00
local multiply = sqrt ( subtr.x * subtr.x + subtr.y * subtr.y )
2022-07-10 19:41:43 +02:00
if ( multiply > max_abs ) then
local close = max_abs / multiply
subtr = { x = subtr.x * close , y = subtr.y * close }
end
if value then
subtr.y = subtr.y * scalar
end
2022-07-10 20:27:18 +02:00
return { x = self.position . x + subtr.x , y = self.position . y + subtr.y }
2022-07-10 19:41:43 +02:00
end
--- If a render is stuck, give it a new position.
2022-07-10 20:27:18 +02:00
function Public : switch_position ( )
2022-07-10 19:41:43 +02:00
if random ( ) < 0.4 then
2022-07-10 20:27:18 +02:00
self.target_position = self : random_position ( )
2022-07-10 19:41:43 +02:00
else
2022-07-10 20:27:18 +02:00
local surface = game.get_surface ( self.surface_id )
2023-04-07 01:31:15 +02:00
if not surface or not surface.valid then
return
end
2022-07-10 19:41:43 +02:00
local chunk = surface.get_random_chunk ( )
2022-07-10 20:27:18 +02:00
self.target_position = { x = ( chunk.x + math.random ( ) ) * 32 , y = ( chunk.y + math.random ( ) ) * 32 }
2022-07-10 19:41:43 +02:00
end
end
2022-07-10 21:37:11 +02:00
--- Notifies for a new render
function Public : notify_new_beam ( )
if not self.notify then
self.notify = true
local surface = game.get_surface ( self.surface_id )
2023-04-07 01:31:15 +02:00
if not surface or not surface.valid then
return
end
2023-09-23 23:00:51 +02:00
game.print ( ' [color=yellow][Orbital][/color] A new orbital strike has been spotted at: [gps= ' .. self.position . x .. ' , ' .. self.position . y .. ' , ' .. surface.name .. ' ] ' )
2022-07-10 21:37:11 +02:00
end
end
--- Renders a new chart
function Public : render_chart ( )
if self.chart then
self.chart . destroy ( )
end
local surface = game.get_surface ( self.surface_id )
2023-04-07 01:31:15 +02:00
if not surface or not surface.valid then
return
end
2022-07-10 21:37:11 +02:00
self.chart =
game.forces [ self.force ] . add_chart_tag (
surface ,
{
icon = { type = ' virtual ' , name = ' signal-info ' } ,
position = self.position ,
text = ' Beam '
}
)
end
2022-07-10 19:41:43 +02:00
--- Sets a new position for a render.
2022-07-10 20:27:18 +02:00
function Public : set_new_position ( )
self.position = self : change_position ( speed , false )
2022-07-10 19:41:43 +02:00
2022-07-10 20:27:18 +02:00
if not self.random_pos_set then
self.random_pos_set = true
self.random_pos_tick = game.tick + 300
2022-07-10 19:41:43 +02:00
end
2022-07-10 20:27:18 +02:00
if self.position . x == self.target_position . x and self.position . y == self.target_position . y then
self : switch_position ( )
2022-07-10 19:41:43 +02:00
end
2022-07-10 20:27:18 +02:00
if self : validate ( ) then
rendering.set_target ( self.render_id , self.position )
self : set_render_scalar_size ( )
2022-07-10 19:41:43 +02:00
end
end
--- Creates fire flame.
2022-07-10 20:27:18 +02:00
function Public : render_fire_damage ( )
2022-07-10 19:41:43 +02:00
if random ( 1 , 15 ) == 1 then
2022-07-10 20:27:18 +02:00
local surface = game.get_surface ( self.surface_id )
2023-04-07 01:31:15 +02:00
if not surface or not surface.valid then
return
end
2022-07-10 20:27:18 +02:00
surface.create_entity ( { name = ' fire-flame ' , position = { x = self.position . x , y = self.position . y + 5 } } )
2022-07-10 19:41:43 +02:00
if random ( 1 , 5 ) == 1 then
2023-04-07 01:31:15 +02:00
surface.create_entity (
{
name = ' medium-scorchmark ' ,
position = { x = self.position . x , y = self.position . y + 5 } ,
force = ' neutral '
}
)
2022-07-10 19:41:43 +02:00
end
end
end
--- Damages entities nearby.
2022-07-10 20:27:18 +02:00
function Public : damage_entities_nearby ( )
2022-07-10 21:37:11 +02:00
if random ( 1 , 3 ) == 1 then
2022-07-10 20:27:18 +02:00
local surface = game.get_surface ( self.surface_id )
2023-04-07 01:31:15 +02:00
if not surface or not surface.valid then
return
end
2022-07-10 19:41:43 +02:00
local damage = random ( 10 , 15 )
2023-04-07 01:31:15 +02:00
local entities =
surface.find_entities_filtered (
{
position = self.position ,
radius = 20 ,
type = ' simple-entity ' ,
invert = true
}
)
2022-07-10 19:41:43 +02:00
for _ , entity in pairs ( entities ) do
if entity.valid then
if entity.health then
if entity.force . name ~= ' enemy ' then
2023-09-23 23:00:51 +02:00
entity.damage ( damage , ' enemy ' , ' explosion ' )
2022-07-10 19:41:43 +02:00
end
end
end
end
end
end
--- Validates if a render is valid.
2022-07-10 21:37:11 +02:00
---@return boolean|integer
2022-07-10 20:27:18 +02:00
function Public : validate ( )
2022-07-10 21:37:11 +02:00
if not self.render_id then
2023-09-23 23:00:51 +02:00
self : new_render ( )
return false
2022-07-10 21:37:11 +02:00
end
2022-07-10 20:27:18 +02:00
if rendering.is_valid ( self.render_id ) then
2022-07-10 19:41:43 +02:00
return true
end
return false
end
--- Destroys a render.
2022-07-10 20:27:18 +02:00
function Public : destroy_render ( )
if rendering.is_valid ( self.render_id ) then
rendering.destroy ( self.render_id )
2022-07-10 19:41:43 +02:00
end
2022-07-10 20:27:18 +02:00
return self
2022-07-10 19:41:43 +02:00
end
2022-07-10 21:37:11 +02:00
--- Destroys a render.
function Public : destroy_chart ( )
if self.chart then
self.chart . destroy ( )
end
return self
end
2022-07-10 19:41:43 +02:00
--- Removes a render.
2022-07-10 21:37:11 +02:00
function Public : remove_render ( )
2022-07-10 20:27:18 +02:00
self : destroy_render ( )
2022-07-10 21:37:11 +02:00
self : destroy_chart ( )
2022-07-10 19:41:43 +02:00
2022-07-10 21:37:11 +02:00
remove ( this.renders , self.id )
2022-07-10 20:27:18 +02:00
return self
2022-07-10 19:41:43 +02:00
end
2022-07-10 21:37:11 +02:00
function Public : work ( tick )
if tick < self.ttl then
self : render_chart ( )
self : notify_new_beam ( )
self : set_new_position ( )
self : render_fire_damage ( )
self : damage_entities_nearby ( )
if self.random_pos_set and tick > self.random_pos_tick then
self : switch_position ( )
self.random_pos_set = nil
self.random_pos_tick = nil
end
else
self : remove_render ( )
end
end
2022-07-10 19:41:43 +02:00
--- Creates a new render.
---@param sprite string
2023-09-23 23:00:51 +02:00
---@param surface LuaSurface
2022-07-10 20:27:18 +02:00
---@param ttl integer|nil
---@param scalar table|nil
2022-07-10 21:37:11 +02:00
---@param delayed number|nil
2022-07-10 19:41:43 +02:00
---@return table
2022-07-10 21:37:11 +02:00
function Public . new ( sprite , surface , ttl , scalar , delayed )
2022-07-10 20:27:18 +02:00
local render = setmetatable ( { } , Public.metatable )
2022-07-10 19:41:43 +02:00
render.surface_id = surface.index
2022-07-10 20:27:18 +02:00
local position , random_position = render : new_target ( )
render.position = position
2022-07-10 19:41:43 +02:00
render.sprite = sprite
2022-07-10 21:37:11 +02:00
render.force = ' player '
2022-07-10 19:41:43 +02:00
render.target_position = random_position
2022-07-10 21:37:11 +02:00
render.id = # this.renders + 1
if delayed then
render.delayed = game.tick + delayed
render.ttl = ttl or ( game.tick + delayed ) + 7200 -- 2 minutes duration
else
render.ttl = ttl or game.tick + 7200 -- 2 minutes duration
render : validate ( )
if not scalar then
render : set_render_scalar_size ( )
end
2022-07-10 19:41:43 +02:00
end
2022-07-10 21:37:11 +02:00
render.ttl = ttl or game.tick + 7200 -- 2 minutes duration
2022-07-10 19:41:43 +02:00
2022-07-10 21:37:11 +02:00
this.renders [ render.id ] = render
2022-07-10 19:41:43 +02:00
return render
end
2022-07-10 21:37:11 +02:00
--- Creates a new defined beam
2023-09-23 23:00:51 +02:00
---@param surface LuaSurface
---@param ttl number|nil
function Public . new_beam ( surface , ttl )
Public.new ( Gui.beam , surface , ttl )
end
--- Defines new targets as valid targets
---@param targets table
function Public . new_valid_targets ( targets )
if targets and type ( targets ) == ' table ' then
this.backup_valid_targets = this.valid_targets
this.valid_targets = targets
else
error ( ' New valid targets needs to be of type table ' , 2 )
end
end
--- Defaults the valid targets
function Public . reset_valid_targets ( )
this.valid_targets = this.backup_valid_targets
2022-07-10 21:37:11 +02:00
end
--- Creates a new defined beam with a delayed action
2023-09-23 23:00:51 +02:00
---@param surface LuaSurface
2022-07-10 21:37:11 +02:00
---@param time number
function Public . new_beam_delayed ( surface , time )
Public.new ( Gui.beam , surface , nil , nil , time )
end
2022-07-10 19:41:43 +02:00
Event.add (
defines.events . on_tick ,
function ( )
if # this.renders == 0 then
return
end
local tick = game.tick
2022-07-10 21:37:11 +02:00
for id = 1 , # this.renders , 1 do
local render = this.renders [ id ]
2022-07-10 19:41:43 +02:00
if render then
2022-07-10 21:37:11 +02:00
if render.delayed then
if tick > render.delayed then
render : work ( tick )
2022-07-10 19:41:43 +02:00
end
else
2022-07-10 21:37:11 +02:00
render : work ( tick )
2022-07-10 19:41:43 +02:00
end
end
end
end
)
2022-07-10 21:37:11 +02:00
if _DEBUG then
commands.add_command (
' laser ' ,
' new laser ' ,
function ( )
local player = game.player
if player and player.valid then
if not player.admin then
return
end
2022-07-10 19:41:43 +02:00
2022-07-10 21:37:11 +02:00
Public.new_beam_delayed ( player.surface , 222 )
2022-07-10 20:27:18 +02:00
end
end
2022-07-10 21:37:11 +02:00
)
end
2022-07-10 20:27:18 +02:00
2022-07-10 19:41:43 +02:00
return Public