2019-02-01 23:46:35 +02:00
--[[
2019-02-03 03:03:44 +02:00
This module restricts the placement of entities and ghosts based on an allowed and banned list ,
as well as by the ( optionally ) provided function .
The table of allowed_entities are * always * allowed to be placed .
The table of banned_entities are * never * allowed to be placed , and are destroyed .
For anything not in either of those lists , you can use the set_keep_alive_callback function to set a keep_alive_callback function .
This means you can use any custom logic you want to determine whether an entity should be destroyed or not .
The callback function is supplied a valid LuaEntity as an argument .
A return of true indicates the entity should be kept alive , while false or nil indicate it should be destroyed .
2019-10-05 17:00:36 +02:00
This function must be a registered with the Token module and the keep_alive_callback function will take the Token - id as parameter
This is to prevent upvalue errors
2019-02-03 03:03:44 +02:00
Refunds for items that were placed can be toggled on or off via the enable and disable_refund functions
2019-02-01 23:46:35 +02:00
2019-02-03 03:03:44 +02:00
Lastly , this module raises 2 events : on_pre_restricted_entity_destroyed and on_restricted_entity_destroyed events .
They are fully defined below .
2019-02-01 23:46:35 +02:00
2019-02-03 03:03:44 +02:00
Examples ( only the first example will include the require ) :
-- A map which allows no roboports:
local RestrictEntities = require ' map_gen.shared.entity_placement_restriction '
RestrictEntities.add_banned ( { ' roboport ' } )
-- A map which allows only belts (for a foot race map, of course)
-- The function provided does nothing but return nil
-- every entity will be destroyed except those on the allowed list
RestrictEntities.add_allowed ( { ' transport-belt ' } )
2019-10-05 17:00:36 +02:00
RestrictEntities.set_keep_alive_callback ( Token.register ( function ( ) end ) )
2019-02-03 03:03:44 +02:00
-- Danger ores (a lot of important code omitted for the sake of a brief example)
RestrictEntities.add_allowed ( { belts , power_poles , mining_drills , ' pumpjack ' } )
RestrictEntities.set_keep_alive_callback (
2019-10-05 17:00:36 +02:00
Token.register (
function ( entity )
if entity.surface . count_entities_filtered { area = entity.bounding_box , type = ' resource ' , limit = 1 } == 0 then
return true
end
2019-02-03 03:03:44 +02:00
end
2019-10-05 17:00:36 +02:00
)
2019-02-03 03:03:44 +02:00
)
2019-02-01 23:46:35 +02:00
] ]
local Event = require ' utils.event '
local Global = require ' utils.global '
local Token = require ' utils.token '
local table = require ' utils.table '
-- Localized functions
local raise_event = script.raise_event
local Public = {
events = {
--[[
2019-02-02 20:33:30 +02:00
on_pre_restricted_entity_destroyed
Called before an entity is destroyed by this script
2019-02-01 23:46:35 +02:00
Contains
name :: defines . events : Identifier of the event
tick :: uint : Tick the event was generated .
player_index :: uint
2019-02-17 07:32:43 +02:00
created_entity :: LuaEntity
2019-02-01 23:46:35 +02:00
ghost :: boolean indicating if the entity was a ghost
2019-02-17 07:32:43 +02:00
stack :: LuaItemStack
2019-02-01 23:46:35 +02:00
] ]
2019-03-03 18:49:16 +02:00
on_pre_restricted_entity_destroyed = Event.generate_event_name ( ' on_pre_restricted_entity_destroyed ' ) ,
2019-02-02 20:33:30 +02:00
--[[
on_restricted_entity_destroyed
Called when an entity is destroyed by this script
Contains
name :: defines . events : Identifier of the event
tick :: uint : Tick the event was generated .
player_index :: uint
2019-02-17 07:32:43 +02:00
player :: LuaPlayer The player who was refunded ( optional )
2019-02-02 20:33:30 +02:00
ghost :: boolean indicating if the entity was a ghost
2019-02-17 07:32:43 +02:00
item_returned :: boolean indicating if a refund of the item was attempted
2019-02-02 20:33:30 +02:00
] ]
2019-03-03 18:49:16 +02:00
on_restricted_entity_destroyed = Event.generate_event_name ( ' on_restricted_entity_destroyed ' )
2019-02-01 23:46:35 +02:00
}
}
-- Global-registered locals
2019-02-02 20:33:30 +02:00
local allowed_entities = { }
local banned_entities = { }
2019-02-01 23:46:35 +02:00
local primitives = {
2019-02-03 03:03:44 +02:00
event = nil , -- if the event is registered or not
refund = true , -- if we issue a refund or not
2019-10-05 19:36:49 +02:00
prevent_tile_bp = false , -- prevents players from placing blueprints with tiles
2019-10-06 14:53:51 +02:00
spill = false , -- spills items from entities with inventories to prevent destroying items when upgrading
keep_alive_callback = nil , -- the token registered function to process entities through
anti_grief_callback = nil -- the token registered function to process anti griefing through
2019-02-01 23:46:35 +02:00
}
Global.register (
{
2019-02-02 20:33:30 +02:00
allowed_entities = allowed_entities ,
banned_entities = banned_entities ,
2019-10-06 14:53:51 +02:00
primitives = primitives
2019-02-01 23:46:35 +02:00
} ,
function ( tbl )
2019-02-02 20:33:30 +02:00
allowed_entities = tbl.allowed_entities
banned_entities = tbl.banned_entities
2019-02-01 23:46:35 +02:00
primitives = tbl.primitives
end
)
-- Local functions
2019-10-06 14:53:51 +02:00
--- Spill items stacks
-- @param entity <LuaEntity> the entity from which the items should be spilled
-- @param item <ItemStackSpecification> the item stack that should be spilled
2019-10-06 01:20:01 +02:00
local function spill_item_stack ( entity , item )
entity.surface . spill_item_stack ( entity.position , item , true , entity.force , false )
end
2019-10-06 22:59:09 +02:00
local Task = require ' utils.task '
--- Cleans the players cursor to prevent from spam replacing entities with inventory
-- Somehow required to have a 1 tick delay before cleaning the players cursor
2020-11-15 22:29:37 +02:00
local delay_clear_cursor =
2019-10-21 17:20:28 +02:00
Token.register (
function ( param )
2020-11-15 22:29:37 +02:00
param.player . clear_cursor ( )
2019-10-21 17:20:28 +02:00
end
)
2019-10-06 22:59:09 +02:00
2019-10-06 14:53:51 +02:00
--- Checks if entity has an inventory with items inside, and spills them on the ground
local function entities_with_inventory ( entity , player )
if primitives.spill and entity.has_items_inside ( ) then
2020-11-15 22:29:37 +02:00
Task.set_timeout_in_ticks ( 1 , delay_clear_cursor , { player = player } )
2019-10-06 14:53:51 +02:00
local type = entity.type
2019-10-06 22:59:09 +02:00
if type == ' container ' then
2019-10-06 14:53:51 +02:00
for item , count in pairs ( entity.get_inventory ( defines.inventory . chest ) . get_contents ( ) ) do
2019-10-21 17:20:28 +02:00
spill_item_stack ( entity , { name = item , count = count } )
2019-10-06 14:53:51 +02:00
end
2019-10-06 22:59:09 +02:00
elseif type == ' logistic-container ' then
2019-10-21 17:20:28 +02:00
entity.surface . create_entity { name = ' steel-chest ' , position = entity.position , direction = entity.direction , force = entity.force , fast_replace = true , spill = false }
2019-10-06 22:59:09 +02:00
if player and player.valid and primitives.refund then -- refunding materials required to make a logistic container minus the "free" steel-chest generated above
2019-10-21 17:20:28 +02:00
player.insert ( { name = ' electronic-circuit ' , count = 3 } )
player.insert ( { name = ' advanced-circuit ' , count = 1 } )
2019-10-06 22:59:09 +02:00
end
return true
2019-10-06 14:53:51 +02:00
elseif type == ' furnace ' then
for item , count in pairs ( entity.get_inventory ( defines.inventory . fuel ) . get_contents ( ) ) do
spill_item_stack ( entity , { name = item , count = count } )
end
for item , count in pairs ( entity.get_inventory ( defines.inventory . furnace_result ) . get_contents ( ) ) do
spill_item_stack ( entity , { name = item , count = count } )
end
for item , count in pairs ( entity.get_inventory ( defines.inventory . furnace_source ) . get_contents ( ) ) do
spill_item_stack ( entity , { name = item , count = count } )
end
elseif type == ' assembling-machine ' then
for item , count in pairs ( entity.get_inventory ( defines.inventory . assembling_machine_input ) . get_contents ( ) ) do
spill_item_stack ( entity , { name = item , count = count } )
end
for item , count in pairs ( entity.get_inventory ( defines.inventory . assembling_machine_modules ) . get_contents ( ) ) do
spill_item_stack ( entity , { name = item , count = count } )
end
for item , count in pairs ( entity.get_inventory ( defines.inventory . assembling_machine_output ) . get_contents ( ) ) do
spill_item_stack ( entity , { name = item , count = count } )
end
2019-10-21 17:20:28 +02:00
elseif type == ' ammo-turret ' then
for item , count in pairs ( entity.get_inventory ( defines.inventory . turret_ammo ) . get_contents ( ) ) do
player.insert ( { name = item , count = count } )
end
return -- Prevents triggering when autofill is enabled
2019-10-06 14:53:51 +02:00
end
Token.get ( primitives.anti_grief_callback ) ( entity , player )
end
2019-10-06 22:59:09 +02:00
return false
2019-10-06 14:53:51 +02:00
end
2019-10-06 01:20:01 +02:00
2019-02-03 03:03:44 +02:00
--- Token for the on_built event callback, checks if an entity should be destroyed.
2019-02-01 23:46:35 +02:00
local on_built_token =
Token.register (
function ( event )
local entity = event.created_entity
if not entity or not entity.valid then
return
end
local name = entity.name
if name == ' tile-ghost ' then
2019-10-21 17:20:28 +02:00
if primitives.prevent_tile_bp and entity.ghost_name ~= ' landfill ' then
2019-10-05 19:36:49 +02:00
entity.destroy ( )
end
2019-02-01 23:46:35 +02:00
return
end
local ghost = false
if name == ' entity-ghost ' then
name = entity.ghost_name
ghost = true
end
2019-02-03 06:08:14 +02:00
2019-02-03 03:03:44 +02:00
if allowed_entities [ name ] then
2019-02-01 23:46:35 +02:00
return
end
2019-02-03 03:03:44 +02:00
-- Takes the keep_alive_callback function and runs it with the entity as an argument
-- If true is returned, we exit. If false, we destroy the entity.
2019-02-02 20:33:30 +02:00
local keep_alive_callback = primitives.keep_alive_callback
2019-03-16 20:35:09 +02:00
-- return in these cases:
-- not banned and no callback function
-- not banned and callback function and saved by callback
-- destroy in these cases:
-- all banned ents
-- not banned and callback function and not saved by callback
2019-10-05 17:00:36 +02:00
if not banned_entities [ name ] and ( not keep_alive_callback or Token.get ( keep_alive_callback ) ( entity ) ) then
2019-02-01 23:46:35 +02:00
return
end
2019-02-17 01:12:49 +02:00
local index = event.player_index
2019-02-01 23:46:35 +02:00
2019-02-17 01:12:49 +02:00
local stack = event.stack
raise_event (
Public.events . on_pre_restricted_entity_destroyed ,
{
player_index = index ,
2019-02-17 07:32:43 +02:00
created_entity = entity ,
ghost = ghost ,
stack = stack
2019-02-17 01:12:49 +02:00
}
)
2019-02-03 03:03:44 +02:00
2019-10-06 02:05:01 +02:00
local player = game.get_player ( index )
2019-02-03 03:03:44 +02:00
-- Need to revalidate the entity since we sent it to the raised event
2019-02-02 20:33:30 +02:00
if entity.valid then
2019-10-06 14:53:51 +02:00
-- Checking if the entity has an inventory and spills the content on the ground to prevent destroying those too
2019-10-06 22:59:09 +02:00
if entities_with_inventory ( entity , player ) then
ghost = true -- Cheating to prevent refunds
else
entity.destroy ( )
end
2019-02-01 23:46:35 +02:00
end
2019-02-03 03:03:44 +02:00
-- Check if we issue a refund: make sure refund is enabled, make sure we're not refunding a ghost,
-- and revalidate the stack since we sent it to the raised event
2019-02-17 01:12:49 +02:00
local item_returned
2019-02-17 07:32:43 +02:00
if player and player.valid and primitives.refund and not ghost and stack.valid then
player.insert ( stack )
2019-02-17 01:12:49 +02:00
item_returned = true
2019-02-03 03:03:44 +02:00
else
2019-02-17 01:12:49 +02:00
item_returned = false
2019-02-01 23:46:35 +02:00
end
2019-02-02 20:33:30 +02:00
2019-02-17 01:12:49 +02:00
raise_event (
Public.events . on_restricted_entity_destroyed ,
{
player_index = index ,
2019-02-17 07:32:43 +02:00
player = player ,
2019-02-17 01:12:49 +02:00
ghost = ghost ,
item_returned = item_returned
}
)
2019-02-01 23:46:35 +02:00
end
)
2019-02-03 03:03:44 +02:00
--- Registers and unregisters the event hook
local function check_event_status ( )
-- First we check if the event hook is in place or not
2019-02-01 23:46:35 +02:00
if primitives.event then
2019-02-03 03:03:44 +02:00
-- If there are no items in either list and no function is present, unhook the event
if not next ( allowed_entities ) and not next ( banned_entities ) and not primitives.keep_alive_callback then
Event.remove_removable ( defines.events . on_built_entity , on_built_token )
primitives.event = nil
end
else
-- If either of the lists have an entry or there is a function present, hook the event
if next ( allowed_entities ) or next ( banned_entities ) or primitives.keep_alive_callback then
Event.add_removable ( defines.events . on_built_entity , on_built_token )
primitives.event = true
end
2019-02-01 23:46:35 +02:00
end
end
-- Public functions
2019-02-03 03:03:44 +02:00
--- Sets the keep_alive_callback function. This function is used to provide
-- logic on what entities should and should not be destroyed.
2019-02-02 20:33:30 +02:00
-- @param keep_alive_callback <function>
function Public . set_keep_alive_callback ( keep_alive_callback )
2019-10-05 17:00:36 +02:00
if type ( keep_alive_callback ) ~= ' number ' then
error ( ' Sending a non-token function ' )
2019-02-03 03:03:44 +02:00
end
2019-02-02 20:33:30 +02:00
primitives.keep_alive_callback = keep_alive_callback
2019-02-03 03:03:44 +02:00
check_event_status ( )
end
--- Removes the keep_alive_callback function
function Public . remove_keep_alive_callback ( )
primitives.keep_alive_callback = nil
check_event_status ( )
2019-02-01 23:46:35 +02:00
end
2019-10-06 14:53:51 +02:00
--- Sets the anti_grief_callback function. This function is used to provide
-- logic on what entities should and should not be destroyed.
-- @param anti_grief_callback <function>
function Public . set_anti_grief_callback ( anti_grief_callback )
if type ( anti_grief_callback ) ~= ' number ' then
error ( ' Sending a non-token function ' )
end
primitives.anti_grief_callback = anti_grief_callback
end
--- Removes the anti_grief_callback function
function Public . remove_anti_grief_callback ( )
primitives.anti_grief_callback = nil
end
2019-02-01 23:46:35 +02:00
--- Adds to the list of allowed entities
2019-02-03 03:03:44 +02:00
-- @param ents <table> array of string entity names
2019-02-01 23:46:35 +02:00
function Public . add_allowed ( ents )
for _ , v in pairs ( ents ) do
2019-02-02 20:33:30 +02:00
allowed_entities [ v ] = true
2019-02-01 23:46:35 +02:00
end
2019-02-03 03:03:44 +02:00
check_event_status ( )
2019-02-01 23:46:35 +02:00
end
--- Removes from the list of allowed entities
2019-02-03 03:03:44 +02:00
-- @param ents <table> array of string entity names
2019-02-01 23:46:35 +02:00
function Public . remove_allowed ( ents )
for _ , v in pairs ( ents ) do
2019-02-02 20:33:30 +02:00
allowed_entities [ v ] = nil
2019-02-01 23:46:35 +02:00
end
2019-02-03 03:03:44 +02:00
check_event_status ( )
2019-02-01 23:46:35 +02:00
end
2019-02-03 03:03:44 +02:00
--- Resets the list of allowed entities
2019-02-01 23:46:35 +02:00
function Public . reset_allowed ( )
2019-02-02 20:33:30 +02:00
table.clear_table ( allowed_entities )
2019-02-03 03:03:44 +02:00
check_event_status ( )
2019-02-01 23:46:35 +02:00
end
--- Adds to the list of banned entities
2019-02-03 03:03:44 +02:00
-- @param ents <table> array of string entity names
2019-02-01 23:46:35 +02:00
function Public . add_banned ( ents )
for _ , v in pairs ( ents ) do
2019-02-02 20:33:30 +02:00
banned_entities [ v ] = true
2019-02-01 23:46:35 +02:00
end
2019-02-03 03:03:44 +02:00
check_event_status ( )
2019-02-01 23:46:35 +02:00
end
--- Removes from the list of banned entities
2019-02-03 03:03:44 +02:00
-- @param ents <table> array of string entity names
2019-02-01 23:46:35 +02:00
function Public . remove_banned ( ents )
for _ , v in pairs ( ents ) do
2019-02-02 20:33:30 +02:00
banned_entities [ v ] = nil
2019-02-01 23:46:35 +02:00
end
2019-02-03 03:03:44 +02:00
check_event_status ( )
2019-02-01 23:46:35 +02:00
end
--- Resets the list of banned entities
function Public . reset_banned ( )
2019-02-02 20:33:30 +02:00
table.clear_table ( banned_entities )
2019-02-03 03:03:44 +02:00
check_event_status ( )
end
--- Enables the returning of items that are destroyed by this module
function Public . enable_refund ( )
primitives.refund = true
end
--- Disables the returning of items that are destroyed by this module
function Public . set_refund ( )
primitives.refund = false
2019-02-01 23:46:35 +02:00
end
2019-10-06 14:53:51 +02:00
--- Enables the ability to blueprint tiles (Landfill always enabled)
2019-10-05 19:36:49 +02:00
function Public . enable_tile_bp ( )
primitives.prevent_tile_bp = false
end
2019-10-06 14:53:51 +02:00
--- Disables the ability to blueprint tiles (Landfill always enabled)
2019-10-05 19:36:49 +02:00
function Public . set_tile_bp ( )
primitives.prevent_tile_bp = true
end
2019-10-06 14:53:51 +02:00
--- Enables the spill function
function Public . enable_spill ( )
primitives.spill = true
end
--- Disables the spill function
function Public . set_spill ( )
primitives.spill = false
end
2019-02-01 23:46:35 +02:00
return Public