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 = {
|
|
|
|
renders = {}
|
|
|
|
}
|
|
|
|
|
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 target_entities = {
|
|
|
|
'character',
|
|
|
|
'tank',
|
|
|
|
'car',
|
|
|
|
'radar',
|
|
|
|
'lab',
|
|
|
|
'furnace',
|
|
|
|
'locomotive',
|
|
|
|
'cargo-wagon',
|
|
|
|
'fluid-wagon',
|
|
|
|
'artillery-wagon',
|
|
|
|
'artillery-turret',
|
|
|
|
'laser-turret',
|
|
|
|
'gun-turret',
|
|
|
|
'flamethrower-turret',
|
|
|
|
'silo',
|
|
|
|
'spidertron'
|
|
|
|
}
|
|
|
|
|
|
|
|
local sqrt = math.sqrt
|
|
|
|
local random = math.random
|
|
|
|
local remove = table.remove
|
|
|
|
local speed = 0.06
|
|
|
|
|
|
|
|
--- Draws a new render.
|
|
|
|
---@return integer
|
2022-07-10 20:27:18 +02:00
|
|
|
function Public:new_render()
|
|
|
|
local surface = game.get_surface(self.surface_id)
|
|
|
|
return rendering.draw_sprite {target = self.position, sprite = self.sprite, surface = surface}
|
2022-07-10 19:41:43 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
--- Sets a new target for a given render.
|
|
|
|
---@return table
|
|
|
|
---@return table
|
2022-07-10 20:27:18 +02:00
|
|
|
function Public:new_target()
|
|
|
|
local surface = game.get_surface(self.surface_id)
|
2022-07-10 19:41:43 +02:00
|
|
|
local position
|
|
|
|
local entities = surface.find_entities_filtered {type = target_entities}
|
|
|
|
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()
|
|
|
|
if not self.target_id then
|
2022-07-10 19:41:43 +02:00
|
|
|
return
|
|
|
|
end
|
|
|
|
|
2022-07-10 20:27:18 +02:00
|
|
|
rendering.set_y_scale(self.target_id, 3.5) -- 1.5
|
|
|
|
rendering.set_x_scale(self.target_id, 7) -- 2
|
2022-07-10 19:41:43 +02:00
|
|
|
rendering.set_color(
|
2022-07-10 20:27:18 +02:00
|
|
|
self.target_id,
|
2022-07-10 19:41:43 +02:00
|
|
|
{
|
|
|
|
r = 1,
|
|
|
|
g = 0.7,
|
|
|
|
b = 0.7
|
|
|
|
}
|
|
|
|
)
|
|
|
|
end
|
|
|
|
--- 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)
|
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
|
|
|
|
|
|
|
|
--- 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)
|
|
|
|
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
|
2022-07-10 20:27:18 +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 19:41:43 +02:00
|
|
|
if random(1, 5) == 1 then
|
2022-07-10 20:27:18 +02:00
|
|
|
local surface = game.get_surface(self.surface_id)
|
2022-07-10 19:41:43 +02:00
|
|
|
local radius = 10
|
|
|
|
local damage = random(10, 15)
|
2022-07-10 20:27:18 +02:00
|
|
|
local entities = surface.find_entities_filtered({area = {{self.position.x - radius - 4, self.position.y - radius - 6}, {self.position.x + radius + 4, self.position.y + radius + 6}}})
|
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
|
|
|
|
if entity.name == 'character' then
|
|
|
|
entity.damage(damage, 'enemy')
|
|
|
|
else
|
|
|
|
entity.health = entity.health - damage
|
|
|
|
if entity.health <= 0 then
|
|
|
|
entity.die('enemy')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--- Validates if a render is valid.
|
|
|
|
---@return boolean
|
2022-07-10 20:27:18 +02:00
|
|
|
function Public:validate()
|
|
|
|
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
|
|
|
|
|
|
|
|
--- Removes a render.
|
|
|
|
---@param id integer
|
2022-07-10 20:27:18 +02:00
|
|
|
function Public:remove_render(id)
|
|
|
|
self:destroy_render()
|
2022-07-10 19:41:43 +02:00
|
|
|
|
|
|
|
remove(this.renders, id)
|
2022-07-10 20:27:18 +02:00
|
|
|
return self
|
2022-07-10 19:41:43 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
--- Creates a new render.
|
|
|
|
---@param sprite string
|
|
|
|
---@param surface userdata
|
2022-07-10 20:27:18 +02:00
|
|
|
---@param ttl integer|nil
|
|
|
|
---@param scalar table|nil
|
2022-07-10 19:41:43 +02:00
|
|
|
---@return table
|
|
|
|
function Public.new(sprite, surface, ttl, scalar)
|
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
|
|
|
|
render.target_position = random_position
|
2022-07-10 20:27:18 +02:00
|
|
|
render.render_id = render:new_render()
|
2022-07-10 19:41:43 +02:00
|
|
|
render.ttl = ttl or game.tick + 7200 -- 2 minutes duration
|
|
|
|
if not scalar then
|
2022-07-10 20:27:18 +02:00
|
|
|
render:set_render_scalar_size()
|
2022-07-10 19:41:43 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
this.renders[#this.renders + 1] = render
|
|
|
|
|
|
|
|
return render
|
|
|
|
end
|
|
|
|
|
|
|
|
Event.add(
|
|
|
|
defines.events.on_tick,
|
|
|
|
function()
|
|
|
|
if #this.renders == 0 then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
local tick = game.tick
|
|
|
|
|
|
|
|
for id, render in pairs(this.renders) do
|
|
|
|
if render then
|
|
|
|
if tick < render.ttl then
|
2022-07-10 20:27:18 +02:00
|
|
|
render:set_new_position()
|
|
|
|
render:render_fire_damage()
|
|
|
|
render:damage_entities_nearby()
|
2022-07-10 19:41:43 +02:00
|
|
|
if render.random_pos_set and tick > render.random_pos_tick then
|
2022-07-10 20:27:18 +02:00
|
|
|
render:switch_position()
|
2022-07-10 19:41:43 +02:00
|
|
|
render.random_pos_set = nil
|
|
|
|
render.random_pos_tick = nil
|
|
|
|
end
|
|
|
|
else
|
2022-07-10 20:27:18 +02:00
|
|
|
render:remove_render(id)
|
2022-07-10 19:41:43 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
)
|
|
|
|
|
2022-07-10 20:27:18 +02:00
|
|
|
commands.add_command(
|
|
|
|
'laser',
|
|
|
|
'new laser',
|
|
|
|
function()
|
|
|
|
local player = game.player
|
|
|
|
if player and player.valid then
|
|
|
|
if not player.admin then
|
|
|
|
return
|
|
|
|
end
|
|
|
|
|
|
|
|
Public.new(Gui.beam, player.surface)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
)
|
|
|
|
|
2022-07-10 19:41:43 +02:00
|
|
|
return Public
|