--[[-- Commands Module - Protection - Adds commands that can add and remove protection @commands Protection ]] local ExpUtil = require("modules/exp_util") local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event local Storage = require("modules/exp_util/storage") local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles local Commands = require("modules.exp_legacy.expcore.commands") --- @dep expcore.commands local format_player_name = ExpUtil.format_player_name_locale --- @dep expcore.common local EntityProtection = require("modules.exp_legacy.modules.control.protection") --- @dep modules.control.protection local Selection = require("modules.exp_legacy.modules.control.selection") --- @dep modules.control.selection local SelectionProtectEntity = "ProtectEntity" local SelectionProtectArea = "ProtectArea" local renders = {} -- Stores all renders for a player Storage.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(aabb_one, aabb_two) return aabb_point_enclosed(aabb_one.left_top, aabb_two) and aabb_point_enclosed(aabb_one.right_bottom, aabb_two) 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] --[[@as LuaRenderObject]] if render and render.valid then render.destroy() end renders[player.index][key] = nil end --- Toggles entity protection selection -- @command protect-entity Commands.new_command("protect-entity", { "expcom-protection.description-pe" }, "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", { "expcom-protection.description-pa" }, "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.players[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.players[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.players[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.players[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.players[event.player_index] -- Intentionally left as player.surface to allow use in remote view 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 _, render in pairs(renders[event.player_index]) do if render.valid then render.destroy() 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_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)