mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2025-12-27 11:35:22 +09:00
Merge pull request #213 from Cooldude2606/feature/entity-alert
Entity protection
This commit is contained in:
@@ -30,6 +30,7 @@ return {
|
||||
'modules.commands.home',
|
||||
'modules.commands.connect',
|
||||
'modules.commands.last-location',
|
||||
'modules.commands.protection',
|
||||
'modules.commands.spectate',
|
||||
'modules.commands.search',
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
-- @config Discord-Alerts
|
||||
|
||||
return {
|
||||
entity_protection=true,
|
||||
player_reports=true,
|
||||
player_warnings=true,
|
||||
player_bans=true,
|
||||
|
||||
@@ -99,6 +99,8 @@ Roles.new_role('Trainee','TrMod')
|
||||
'command/give-warning',
|
||||
'command/get-warnings',
|
||||
'command/get-reports',
|
||||
'command/protect-entity',
|
||||
'command/protect-area',
|
||||
'command/jail',
|
||||
'command/unjail',
|
||||
'command/kick',
|
||||
@@ -219,7 +221,8 @@ Roles.new_role('Regular','Reg')
|
||||
'command/rainbow',
|
||||
'command/go-to-spawn',
|
||||
'command/me',
|
||||
'standard-decon'
|
||||
'standard-decon',
|
||||
'bypass-entity-protection'
|
||||
}
|
||||
:set_auto_assign_condition(function(player)
|
||||
if player.online_time >= hours3 then
|
||||
|
||||
19
config/protection.lua
Normal file
19
config/protection.lua
Normal file
@@ -0,0 +1,19 @@
|
||||
return {
|
||||
ignore_admins = true, --- @setting ignore_admins If admins are ignored by the protection filter
|
||||
ignore_permission = 'bypass-entity-protection', --- @setting ignore_permission Players with this permission will be ignored by the protection filter, leave nil if expcore.roles is not used
|
||||
repeat_count = 5, --- @setting repeat_count Number of protected entities that must be removed within repeat_lifetime in order to trigger repeated removal protection
|
||||
repeat_lifetime = 3600*20, --- @setting repeat_lifetime The length of time, in ticks, that protected removals will be remembered for
|
||||
refresh_rate = 3600*5, --- @setting refresh_rate How often the age of protected removals are checked against repeat_lifetime
|
||||
always_protected_names = { --- @setting always_protected_names Names of entities which are always protected
|
||||
|
||||
},
|
||||
always_protected_types = { --- @setting always_protected_types Types of entities which are always protected
|
||||
'boiler', 'generator', 'offshore-pump', 'power-switch', 'reactor', 'rocket-silo'
|
||||
},
|
||||
always_trigger_repeat_names = { --- @setting always_trigger_repeat_names Names of entities which always trigger repeated removal protection
|
||||
|
||||
},
|
||||
always_trigger_repeat_types = { --- @setting always_trigger_repeat_types Types of entities which always trigger repeated removal protection
|
||||
'reactor', 'rocket-silo'
|
||||
}
|
||||
}
|
||||
@@ -85,6 +85,16 @@ none-matching=No servers were found with that name, if you used an address pleas
|
||||
[expcom-lastlocation]
|
||||
response=Last location of __1__ was [gps=__2__,__3__]
|
||||
|
||||
[expcom-protection]
|
||||
entered-entity-selection=Entered entity selection, select entites to protect, hold shift to remove protection.
|
||||
entered-area-selection=Entered area selection, select areas to protect, hold shift to remove protection.
|
||||
protected-entities=__1__ entities have been protected.
|
||||
unprotected-entities=__1__ entities have been unprotected.
|
||||
already-protected=This area is already protected.
|
||||
protected-area=This area is now protected.
|
||||
unprotected-area=This area is now unprotected.
|
||||
repeat-offence=__1__ has removed __2__ at [gps=__3__,__4__]
|
||||
|
||||
[expcom-spectate]
|
||||
follow-self=You can not follow yourself
|
||||
|
||||
|
||||
@@ -76,6 +76,21 @@ local function emit_event(args)
|
||||
})
|
||||
end
|
||||
|
||||
--- Repeated protected entity mining
|
||||
if config.entity_protection then
|
||||
local EntityProtection = require 'modules.control.protection' --- @dep modules.control.protection
|
||||
Event.add(EntityProtection.events.on_repeat_violation, function(event)
|
||||
local player_name = get_player_name(event)
|
||||
emit_event{
|
||||
title='Entity Protection',
|
||||
description='A player removed protected entities',
|
||||
color=Colors.yellow,
|
||||
['Player']='<inline>'..player_name,
|
||||
['Entity']='<inline>'..event.entity.name
|
||||
}
|
||||
end)
|
||||
end
|
||||
|
||||
--- Reports added and removed
|
||||
if config.player_reports then
|
||||
local Reports = require 'modules.control.reports' --- @dep modules.control.reports
|
||||
|
||||
216
modules/commands/protection.lua
Normal file
216
modules/commands/protection.lua
Normal file
@@ -0,0 +1,216 @@
|
||||
--[[-- Commands Module - Protection
|
||||
- Adds commands that can add and remove protection
|
||||
@commands Protection
|
||||
]]
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
local Commands = require 'expcore.commands' --- @dep expcore.commands
|
||||
local format_chat_player_name = _C.format_chat_player_name --- @dep expcore.common
|
||||
local EntityProtection = require 'modules.control.protection' --- @dep modules.control.protection
|
||||
local Selection = require 'modules.control.selection' --- @dep modules.control.selection
|
||||
|
||||
local SelectionProtectEntity = 'ProtectEntity'
|
||||
local SelectionProtectArea = 'ProtectArea'
|
||||
|
||||
local renders = {} -- Stores all renders for a player
|
||||
Global.register({
|
||||
renders = renders
|
||||
}, function(tbl)
|
||||
renders = tbl.renders
|
||||
end)
|
||||
|
||||
--- Test if a point is inside an aabb
|
||||
local function aabb_point_enclosed(point, aabb)
|
||||
return point.x >= aabb.left_top.x and point.y >= aabb.left_top.y
|
||||
and point.x <= aabb.right_bottom.x and point.y <= aabb.right_bottom.y
|
||||
end
|
||||
|
||||
--- Test if an aabb is inside another aabb
|
||||
local function aabb_area_enclosed(aabbOne, aabbTwo)
|
||||
return aabb_point_enclosed(aabbOne.left_top, aabbTwo)
|
||||
and aabb_point_enclosed(aabbOne.right_bottom, aabbTwo)
|
||||
end
|
||||
|
||||
--- Align an aabb to the grid by expanding it
|
||||
local function aabb_align_expand(aabb)
|
||||
return {
|
||||
left_top = { x = math.floor(aabb.left_top.x), y = math.floor(aabb.left_top.y) },
|
||||
right_bottom = { x = math.ceil(aabb.right_bottom.x), y = math.ceil(aabb.right_bottom.y) }
|
||||
}
|
||||
end
|
||||
|
||||
--- Get the key used in protected_entities
|
||||
local function get_entity_key(entity)
|
||||
return string.format('%i,%i', math.floor(entity.position.x), math.floor(entity.position.y))
|
||||
end
|
||||
|
||||
--- Get the key used in protected_areas
|
||||
local function get_area_key(area)
|
||||
return string.format('%i,%i', math.floor(area.left_top.x), math.floor(area.left_top.y))
|
||||
end
|
||||
|
||||
|
||||
--- Show a protected entity to a player
|
||||
local function show_protected_entity(player, entity)
|
||||
local key = get_entity_key(entity)
|
||||
if renders[player.index][key] then return end
|
||||
local rb = entity.selection_box.right_bottom
|
||||
local render_id = rendering.draw_sprite{
|
||||
sprite = 'utility/notification',
|
||||
target = entity,
|
||||
target_offset = {
|
||||
(rb.x-entity.position.x)*0.75,
|
||||
(rb.y-entity.position.y)*0.75
|
||||
},
|
||||
x_scale = 2,
|
||||
y_scale = 2,
|
||||
surface = entity.surface,
|
||||
players = { player }
|
||||
}
|
||||
renders[player.index][key] = render_id
|
||||
end
|
||||
|
||||
--- Show a protected area to a player
|
||||
local function show_protected_area(player, surface, area)
|
||||
local key = get_area_key(area)
|
||||
if renders[player.index][key] then return end
|
||||
local render_id = rendering.draw_rectangle{
|
||||
color = {1, 1, 0, 0.5},
|
||||
filled = false,
|
||||
width = 3,
|
||||
left_top = area.left_top,
|
||||
right_bottom = area.right_bottom,
|
||||
surface = surface,
|
||||
players = { player }
|
||||
}
|
||||
renders[player.index][key] = render_id
|
||||
end
|
||||
|
||||
--- Remove a render object for a player
|
||||
local function remove_render(player, key)
|
||||
local render = renders[player.index][key]
|
||||
if render and rendering.is_valid(render) then rendering.destroy(render) end
|
||||
renders[player.index][key] = nil
|
||||
end
|
||||
|
||||
--- Toggles entity protection selection
|
||||
-- @command protect-entity
|
||||
Commands.new_command('protect-entity', 'Toggles entity protection selection, hold shift to remove protection')
|
||||
:add_alias('pe')
|
||||
:register(function(player)
|
||||
if Selection.is_selecting(player, SelectionProtectEntity) then
|
||||
Selection.stop(player)
|
||||
else
|
||||
Selection.start(player, SelectionProtectEntity)
|
||||
return Commands.success{'expcom-protection.entered-entity-selection'}
|
||||
end
|
||||
end)
|
||||
|
||||
--- Toggles area protection selection
|
||||
-- @command protect-area
|
||||
Commands.new_command('protect-area', 'Toggles area protection selection, hold shift to remove protection')
|
||||
:add_alias('pa')
|
||||
:register(function(player)
|
||||
if Selection.is_selecting(player, SelectionProtectArea) then
|
||||
Selection.stop(player)
|
||||
else
|
||||
Selection.start(player, SelectionProtectArea)
|
||||
return Commands.success{'expcom-protection.entered-area-selection'}
|
||||
end
|
||||
end)
|
||||
|
||||
--- When an area is selected to add protection to entities
|
||||
Selection.on_selection(SelectionProtectEntity, function(event)
|
||||
local player = game.get_player(event.player_index)
|
||||
for _, entity in ipairs(event.entities) do
|
||||
EntityProtection.add_entity(entity)
|
||||
show_protected_entity(player, entity)
|
||||
end
|
||||
player.print{'expcom-protection.protected-entities', #event.entities}
|
||||
end)
|
||||
|
||||
--- When an area is selected to remove protection from entities
|
||||
Selection.on_alt_selection(SelectionProtectEntity, function(event)
|
||||
local player = game.get_player(event.player_index)
|
||||
for _, entity in ipairs(event.entities) do
|
||||
EntityProtection.remove_entity(entity)
|
||||
remove_render(player, get_entity_key(entity))
|
||||
end
|
||||
player.print{'expcom-protection.unprotected-entities', #event.entities}
|
||||
end)
|
||||
|
||||
--- When an area is selected to add protection to the area
|
||||
Selection.on_selection(SelectionProtectArea, function(event)
|
||||
local area = aabb_align_expand(event.area)
|
||||
local areas = EntityProtection.get_areas(event.surface)
|
||||
local player = game.get_player(event.player_index)
|
||||
for _, next_area in pairs(areas) do
|
||||
if aabb_area_enclosed(area, next_area) then
|
||||
return player.print{'expcom-protection.already-protected'}
|
||||
end
|
||||
end
|
||||
EntityProtection.add_area(event.surface, area)
|
||||
show_protected_area(player, event.surface, area)
|
||||
player.print{'expcom-protection.protected-area'}
|
||||
end)
|
||||
|
||||
--- When an area is selected to remove protection from the area
|
||||
Selection.on_alt_selection(SelectionProtectArea, function(event)
|
||||
local area = aabb_align_expand(event.area)
|
||||
local areas = EntityProtection.get_areas(event.surface)
|
||||
local player = game.get_player(event.player_index)
|
||||
for _, next_area in pairs(areas) do
|
||||
if aabb_area_enclosed(next_area, area) then
|
||||
EntityProtection.remove_area(event.surface, next_area)
|
||||
player.print{'expcom-protection.unprotected-area'}
|
||||
remove_render(player, get_area_key(next_area))
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- When selection starts show all protected entities and protected areas
|
||||
Event.add(Selection.events.on_player_selection_start, function(event)
|
||||
if event.selection ~= SelectionProtectEntity and event.selection ~= SelectionProtectArea then return end
|
||||
local player = game.get_player(event.player_index)
|
||||
local surface = player.surface
|
||||
renders[player.index] = {}
|
||||
-- Show protected entities
|
||||
local entities = EntityProtection.get_entities(surface)
|
||||
for _, entity in pairs(entities) do
|
||||
show_protected_entity(player, entity)
|
||||
end
|
||||
-- Show always protected entities by name
|
||||
if #EntityProtection.protected_entity_names > 0 then
|
||||
for _, entity in pairs(surface.find_entities_filtered{ name = EntityProtection.protected_entity_names, force = player.force }) do
|
||||
show_protected_entity(player, entity)
|
||||
end
|
||||
end
|
||||
-- Show always protected entities by type
|
||||
if #EntityProtection.protected_entity_types > 0 then
|
||||
for _, entity in pairs(surface.find_entities_filtered{ type = EntityProtection.protected_entity_types, force = player.force }) do
|
||||
show_protected_entity(player, entity)
|
||||
end
|
||||
end
|
||||
-- Show protected areas
|
||||
local areas = EntityProtection.get_areas(surface)
|
||||
for _, area in pairs(areas) do
|
||||
show_protected_area(player, surface, area)
|
||||
end
|
||||
end)
|
||||
|
||||
--- When selection ends hide protected entities and protected areas
|
||||
Event.add(Selection.events.on_player_selection_end, function(event)
|
||||
if event.selection ~= SelectionProtectEntity and event.selection ~= SelectionProtectArea then return end
|
||||
for _, id in pairs(renders[event.player_index]) do
|
||||
if rendering.is_valid(id) then rendering.destroy(id) end
|
||||
end
|
||||
renders[event.player_index] = nil
|
||||
end)
|
||||
|
||||
--- When there is a repeat offence print it in chat
|
||||
Event.add(EntityProtection.events.on_repeat_violation, function(event)
|
||||
local player_name = format_chat_player_name(event.player_index)
|
||||
Roles.print_to_roles_higher('Regular', {'expcom-protection.repeat-offence', player_name, event.entity.localised_name, event.entity.position.x, event.entity.position.y})
|
||||
end)
|
||||
206
modules/control/protection.lua
Normal file
206
modules/control/protection.lua
Normal file
@@ -0,0 +1,206 @@
|
||||
--[[-- Control Module - Protection
|
||||
- Controls protected entities
|
||||
@control Protection
|
||||
@alias Protection
|
||||
]]
|
||||
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local config = require 'config.protection' --- @dep config.protection
|
||||
local EntityProtection = {
|
||||
protected_entity_names = table.deep_copy(config.always_protected_names),
|
||||
protected_entity_types = table.deep_copy(config.always_protected_types),
|
||||
events = {
|
||||
--- When a player mines a protected entity
|
||||
-- @event on_player_mined_protected
|
||||
-- @tparam number player_index the player index of the player who got mined the entity
|
||||
-- @tparam LuaEntity entity the entity which was mined
|
||||
on_player_mined_protected = script.generate_event_name(),
|
||||
--- When a player repeatedly mines protected entities
|
||||
-- @event on_repeat_violation
|
||||
-- @tparam number player_index the player index of the player who got mined the entities
|
||||
-- @tparam LuaEntity entity the last entity which was mined
|
||||
on_repeat_violation = script.generate_event_name(),
|
||||
}
|
||||
}
|
||||
|
||||
-- Convert config tables into lookup tables
|
||||
for _, config_key in ipairs{'always_protected_names', 'always_protected_types', 'always_trigger_repeat_names', 'always_trigger_repeat_types'} do
|
||||
local tbl = config[config_key]
|
||||
for key, value in ipairs(tbl) do
|
||||
tbl[key] = nil
|
||||
tbl[value] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Require roles if a permission is assigned in the config
|
||||
local Roles
|
||||
if config.ignore_permission then
|
||||
Roles = require 'expcore.roles' --- @dep expcore.roles
|
||||
end
|
||||
|
||||
----- Global Variables -----
|
||||
--- Variables stored in the global table
|
||||
|
||||
local protected_entities = {} -- All entities which are protected
|
||||
local protected_areas = {} -- All areas which are protected
|
||||
local repeats = {} -- Stores repeat removals by players
|
||||
|
||||
Global.register({
|
||||
protected_entities = protected_entities,
|
||||
protected_areas = protected_areas,
|
||||
repeats = repeats
|
||||
}, function(tbl)
|
||||
protected_entities = tbl.protected_entities
|
||||
protected_areas = tbl.protected_areas
|
||||
repeats = tbl.repeats
|
||||
end)
|
||||
|
||||
----- Local Functions -----
|
||||
--- Functions used internally to search and add to the protected array
|
||||
|
||||
--- Get the key used in protected_entities
|
||||
local function get_entity_key(entity)
|
||||
return string.format('%i,%i', math.floor(entity.position.x), math.floor(entity.position.y))
|
||||
end
|
||||
|
||||
--- Get the key used in protected_areas
|
||||
local function get_area_key(area)
|
||||
return string.format('%i,%i', math.floor(area.left_top.x), math.floor(area.left_top.y))
|
||||
end
|
||||
|
||||
--- Check if an entity is always protected
|
||||
local function check_always_protected(entity)
|
||||
return config.always_protected_names[entity.name] or config.always_protected_types[entity.type] or false
|
||||
end
|
||||
|
||||
--- Check if an entity always triggers repeat protection
|
||||
local function check_always_trigger_repeat(entity)
|
||||
return config.always_trigger_repeat_names[entity.name] or config.always_trigger_repeat_types[entity.type] or false
|
||||
end
|
||||
|
||||
----- Public Functions -----
|
||||
--- Functions used to add and remove protected entities
|
||||
|
||||
--- Add an entity to the protected list
|
||||
function EntityProtection.add_entity(entity)
|
||||
local entities = protected_entities[entity.surface.index]
|
||||
if not entities then
|
||||
entities = {}
|
||||
protected_entities[entity.surface.index] = entities
|
||||
end
|
||||
entities[get_entity_key(entity)] = entity
|
||||
end
|
||||
|
||||
--- Remove an entity from the protected list
|
||||
function EntityProtection.remove_entity(entity)
|
||||
local entities = protected_entities[entity.surface.index]
|
||||
if not entities then return end
|
||||
entities[get_entity_key(entity)] = nil
|
||||
end
|
||||
|
||||
--- Get all protected entities on a surface
|
||||
function EntityProtection.get_entities(surface)
|
||||
return protected_entities[surface.index] or {}
|
||||
end
|
||||
|
||||
--- Check if an entity is protected
|
||||
function EntityProtection.is_entity_protected(entity)
|
||||
if check_always_protected(entity) then return true end
|
||||
local entities = protected_entities[entity.surface.index]
|
||||
if not entities then return false end
|
||||
return entities[get_entity_key(entity)] == entity
|
||||
end
|
||||
|
||||
--- Add an area to the protected list
|
||||
function EntityProtection.add_area(surface, area)
|
||||
local areas = protected_areas[surface.index]
|
||||
if not areas then
|
||||
areas = {}
|
||||
protected_areas[surface.index] = areas
|
||||
end
|
||||
areas[get_area_key(area)] = area
|
||||
end
|
||||
|
||||
--- Remove an area from the protected list
|
||||
function EntityProtection.remove_area(surface, area)
|
||||
local areas = protected_areas[surface.index]
|
||||
if not areas then return end
|
||||
areas[get_area_key(area)] = nil
|
||||
end
|
||||
|
||||
--- Get all protected areas on a surface
|
||||
function EntityProtection.get_areas(surface)
|
||||
return protected_areas[surface.index] or {}
|
||||
end
|
||||
|
||||
--- Check if an entity is protected
|
||||
function EntityProtection.is_position_protected(surface, position)
|
||||
local areas = protected_areas[surface.index]
|
||||
if not areas then return false end
|
||||
for _, area in pairs(areas) do
|
||||
if area.left_top.x <= position.x and area.left_top.y <= position.y
|
||||
and area.right_bottom.x >= position.x and area.right_bottom.y >= position.y
|
||||
then
|
||||
return true
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
----- Events -----
|
||||
--- All events registered by this module
|
||||
|
||||
--- Raise events for protected entities
|
||||
Event.add(defines.events.on_pre_player_mined_item, function(event)
|
||||
local entity = event.entity
|
||||
local player = game.get_player(event.player_index)
|
||||
-- Check if the player should be ignored
|
||||
if config.ignore_admins and player.admin then return end
|
||||
if entity.last_user.index == player.index then return end
|
||||
if config.ignore_permission and Roles.player_allowed(player, config.ignore_permission) then return end
|
||||
|
||||
-- Check if the entity is protected
|
||||
if EntityProtection.is_entity_protected(entity)
|
||||
or EntityProtection.is_position_protected(entity.surface, entity.position)
|
||||
then
|
||||
-- Update repeats
|
||||
local player_repeats = repeats[player.name]
|
||||
if not player_repeats then
|
||||
player_repeats = { last = game.tick, count = 0 }
|
||||
repeats[player.name] = player_repeats
|
||||
end
|
||||
player_repeats.last = game.tick
|
||||
player_repeats.count = player_repeats.count + 1
|
||||
-- Send events
|
||||
event.name = EntityProtection.events.on_player_mined_protected
|
||||
script.raise_event(EntityProtection.events.on_player_mined_protected, event)
|
||||
if check_always_trigger_repeat(entity) or player_repeats.count >= config.repeat_count then
|
||||
player_repeats.count = 0 -- Reset to avoid spamming of events
|
||||
event.name = EntityProtection.events.on_repeat_violation
|
||||
script.raise_event(EntityProtection.events.on_repeat_violation, event)
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- Remove old repeats
|
||||
Event.on_nth_tick(config.refresh_rate, function()
|
||||
local old = game.tick - config.repeat_lifetime
|
||||
for player_name, player_repeats in pairs(repeats) do
|
||||
if player_repeats.last <= old then
|
||||
repeats[player_name] = nil
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
--- When an entity is removed remove it from the protection list
|
||||
local function event_remove_entity(event)
|
||||
EntityProtection.remove_entity(event.entity)
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_pre_player_mined_item, event_remove_entity)
|
||||
Event.add(defines.events.on_robot_pre_mined, event_remove_entity)
|
||||
Event.add(defines.events.on_entity_died, event_remove_entity)
|
||||
Event.add(defines.events.script_raised_destroy, event_remove_entity)
|
||||
|
||||
return EntityProtection
|
||||
172
modules/control/selection.lua
Normal file
172
modules/control/selection.lua
Normal file
@@ -0,0 +1,172 @@
|
||||
--[[-- Control Module - Selection
|
||||
- Controls players who have a selection planner, mostly event handlers
|
||||
@control Selection
|
||||
@alias Selection
|
||||
]]
|
||||
|
||||
local Event = require 'utils.event' --- @dep utils.event
|
||||
local Global = require 'utils.global' --- @dep utils.global
|
||||
local Selection = {
|
||||
events = {
|
||||
--- When a player enters selection mode
|
||||
-- @event on_player_selection_start
|
||||
-- @tparam number player_index the player index of the player who entered selection mode
|
||||
-- @tparam string selection the name of the selection being made
|
||||
on_player_selection_start = script.generate_event_name(),
|
||||
--- When a player leaves selection mode
|
||||
-- @event on_player_selection_end
|
||||
-- @tparam number player_index the player index of the player who left selection mode
|
||||
-- @tparam string selection the name of the selection which ended
|
||||
on_player_selection_end = script.generate_event_name(),
|
||||
}
|
||||
}
|
||||
|
||||
local selection_tool = { name='selection-tool' }
|
||||
|
||||
local selections = {}
|
||||
Global.register({
|
||||
selections = selections
|
||||
}, function(tbl)
|
||||
selections = tbl.selections
|
||||
end)
|
||||
|
||||
--- Let a player select an area by providing a selection planner
|
||||
-- @tparam LuaPlayer player The player to place into selection mode
|
||||
-- @tparam string selection_name The name of the selection to start, used with on_selection
|
||||
-- @tparam[opt=false] boolean single_use When true the selection will stop after first use
|
||||
function Selection.start(player, selection_name, single_use, ...)
|
||||
if not player or not player.valid then return end
|
||||
if selections[player.index] then
|
||||
-- Raise the end event if a selection was already in progress
|
||||
script.raise_event(Selection.events.on_player_selection_end, {
|
||||
name = Selection.events.on_player_selection_end,
|
||||
tick = game.tick,
|
||||
player_index = player.index,
|
||||
selection = selections[player.index].name
|
||||
})
|
||||
end
|
||||
|
||||
-- Set the selection data
|
||||
selections[player.index] = {
|
||||
name = selection_name,
|
||||
arguments = { ... },
|
||||
single_use = single_use == true,
|
||||
character = player.character
|
||||
}
|
||||
|
||||
-- Raise the event
|
||||
script.raise_event(Selection.events.on_player_selection_start, {
|
||||
name = Selection.events.on_player_selection_start,
|
||||
tick = game.tick,
|
||||
player_index = player.index,
|
||||
selection = selection_name
|
||||
})
|
||||
|
||||
-- Give a selection tool if one is not in use
|
||||
if player.cursor_stack.is_selection_tool then return end
|
||||
player.clear_cursor() -- Clear the current item
|
||||
player.cursor_stack.set_stack(selection_tool)
|
||||
|
||||
-- Make a slot to place the selection tool even if inventory is full
|
||||
if not player.character then return end
|
||||
player.character_inventory_slots_bonus = player.character_inventory_slots_bonus + 1
|
||||
player.hand_location = { inventory = defines.inventory.character_main, slot = #player.get_main_inventory() }
|
||||
end
|
||||
|
||||
--- Stop a player selection by removing the selection planner
|
||||
-- @tparam LuaPlayer player The player to exit out of selection mode
|
||||
function Selection.stop(player)
|
||||
if not selections[player.index] then return end
|
||||
local character = selections[player.index].character
|
||||
local selection = selections[player.index].name
|
||||
selections[player.index] = nil
|
||||
|
||||
-- Raise the event
|
||||
script.raise_event(Selection.events.on_player_selection_end, {
|
||||
name = Selection.events.on_player_selection_end,
|
||||
tick = game.tick,
|
||||
player_index = player.index,
|
||||
selection = selection
|
||||
})
|
||||
|
||||
-- Remove the selection tool
|
||||
if player.cursor_stack.is_selection_tool then
|
||||
player.cursor_stack.clear()
|
||||
else
|
||||
player.remove_item(selection_tool)
|
||||
end
|
||||
|
||||
-- Remove the extra slot
|
||||
if character and character == player.character then
|
||||
player.character_inventory_slots_bonus = player.character_inventory_slots_bonus - 1
|
||||
player.hand_location = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Get the selection arguments for a player
|
||||
-- @tparam LuaPlayer player The player to get the selection arguments for
|
||||
function Selection.get_arguments(player)
|
||||
if not selections[player.index] then return end
|
||||
return selections[player.index].arguments
|
||||
end
|
||||
|
||||
--- Test if a player is selecting something
|
||||
-- @tparam LuaPlayer player The player to test
|
||||
-- @tparam[opt] string selection_name If given will only return true if the selection is this selection
|
||||
function Selection.is_selecting(player, selection_name)
|
||||
if selection_name ~= nil then
|
||||
if not selections[player.index] then return false end
|
||||
return selections[player.index].name == selection_name
|
||||
else
|
||||
return player.cursor_stack.is_selection_tool
|
||||
end
|
||||
end
|
||||
|
||||
--- Filter on_player_selected_area to this custom selection, appends the selection arguments
|
||||
-- @tparam string selection_name The name of the selection to listen for
|
||||
-- @tparam function handler The event handler
|
||||
function Selection.on_selection(selection_name, handler)
|
||||
Event.add(defines.events.on_player_selected_area, function(event)
|
||||
local selection = selections[event.player_index]
|
||||
if not selection or selection.name ~= selection_name then return end
|
||||
handler(event, unpack(selection.arguments))
|
||||
end)
|
||||
end
|
||||
|
||||
--- Filter on_player_alt_selected_area to this custom selection, appends the selection arguments
|
||||
-- @param string selection_name The name of the selection to listen for
|
||||
-- @param function handler The event handler
|
||||
function Selection.on_alt_selection(selection_name, handler)
|
||||
Event.add(defines.events.on_player_alt_selected_area, function(event)
|
||||
local selection = selections[event.player_index]
|
||||
if not selection or selection.name ~= selection_name then return end
|
||||
handler(event, unpack(selection.arguments))
|
||||
end)
|
||||
end
|
||||
|
||||
--- Stop selection if the selection tool is removed from the cursor
|
||||
Event.add(defines.events.on_player_cursor_stack_changed, function(event)
|
||||
local player = game.get_player(event.player_index)
|
||||
if player.cursor_stack.is_selection_tool then return end
|
||||
Selection.stop(player)
|
||||
end)
|
||||
--- Stop selection after an event such as death or leaving the game
|
||||
local function stop_after_event(event)
|
||||
local player = game.get_player(event.player_index)
|
||||
Selection.stop(player)
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_pre_player_left_game, stop_after_event)
|
||||
Event.add(defines.events.on_pre_player_died, stop_after_event)
|
||||
|
||||
--- Stop selection after a single use if single_use was true during Selection.start
|
||||
local function stop_after_use(event)
|
||||
if not selections[event.player_index] then return end
|
||||
if not selections[event.player_index].single_use then return end
|
||||
stop_after_event(event)
|
||||
end
|
||||
|
||||
Event.add(defines.events.on_player_selected_area, stop_after_use)
|
||||
Event.add(defines.events.on_player_alt_selected_area, stop_after_use)
|
||||
|
||||
return Selection
|
||||
Reference in New Issue
Block a user