diff --git a/config.lua b/config.lua index 942c9c95..4963abf4 100644 --- a/config.lua +++ b/config.lua @@ -169,7 +169,7 @@ global.config = { -- prints messages when the player joins join_messages = { 'Welcome to this map created by the RedMew team. You can join our discord at: redmew.com/discord', - 'Click the question mark in the top left corner for server information and map details.' + 'Click the infomation icon in the top left corner for server information and map details.' }, cutscene = false, -- format is a table: {{message, weight}, {message, weight}}, where a higher weight has more chance to be shown diff --git a/locale/en/redmew_maps.cfg b/locale/en/redmew_maps.cfg index e87208a1..272ec62f 100644 --- a/locale/en/redmew_maps.cfg +++ b/locale/en/redmew_maps.cfg @@ -154,3 +154,18 @@ switch_msg=Go ahead and pick a quadrant you'd like to help out! force_sync_research=New research complete: train_notice1=## - Your items have been returned to __1__ at: + +# locale linked to concrete jungle +[concrete_jungle] +welcome_popup_title=Welcome to Concrete jungle +welcome_popup_player_name=Hello __1__ +welcome_popup_line_1=Most items can't be placed on the ground! +welcome_popup_line_2=Place stone brick, concrete or reinforced concrete on the ground,\nbefore placing items and machines! +welcome_popup_line_3=More information can be found in the __1__ tab\nin __3__ __2__ + +blueprint=blueprint +blueprint_not_enough_ground_support=Some parts of this __1__ cannot be placed here, they need better ground support! +entity_not_enough_ground_support=__1__ requires at least: __2__ + +anti_grief_kick_reason=Spilling too many items on the ground +anti_grief_jail_reason=You have spilled too many items on the ground, contact an admin diff --git a/map_gen/data/.map_previews/concrete_jungle.png b/map_gen/data/.map_previews/concrete_jungle.png new file mode 100644 index 00000000..b1a04c07 Binary files /dev/null and b/map_gen/data/.map_previews/concrete_jungle.png differ diff --git a/map_gen/maps/concrete_jungle.lua b/map_gen/maps/concrete_jungle.lua new file mode 100644 index 00000000..622bdf12 --- /dev/null +++ b/map_gen/maps/concrete_jungle.lua @@ -0,0 +1,503 @@ +local floor = math.floor +local RestrictEntities = require 'map_gen.shared.entity_placement_restriction' +local Event = require 'utils.event' +local b = require 'map_gen.shared.builders' +local Token = require 'utils.token' +local Color = require 'resources.color_presets' +local Retailer = require 'features.retailer' +local Task = require 'utils.task' +local Popup = require 'features.gui.popup' +local Global = require 'utils.global' +local Report = require 'features.report' +local Rank = require 'features.rank_system' +local Ranks = require 'resources.ranks' + +local redmew_config = global.config + +-- Needed because refined hazard concrete is needed and must not be changed by this module +redmew_config.paint.enabled = false + +local concrete_unlocker = true -- Set to false to disable early unlocking of concrete + +local market_remove_concrete = + Token.register( + function() + Retailer.remove_item('fish_market', 'refined-hazard-concrete') + end +) + +local function on_init() --Out comment stuff you don't want to enable + game.difficulty_settings.technology_price_multiplier = 4 + --game.forces.player.technologies.logistics.researched = true + game.forces.player.technologies.automation.researched = true + Task.set_timeout_in_ticks(100, market_remove_concrete) +end + +if concrete_unlocker then + Event.add( + defines.events.on_research_finished, + function(event) + local p_force = game.forces.player + local r = event.research + + if r.name == 'advanced-material-processing' then + p_force.recipes['concrete'].enabled = true + end + end + ) +end + +local times_spilled = {} +Global.register( + { + times_spilled = times_spilled + }, + function(tbl) + times_spilled = tbl.times_spilled + end +) + +local ScenarioInfo = require 'features.gui.info' + +ScenarioInfo.set_map_name('Concrete Jungle') +ScenarioInfo.set_map_description([[ +Extensive underground mining has resulted in brittle soil. +New regulations requires that heavy objects are placed +on top of proper materials, to support the ground! +]]) + +-- Tiers of tiles definition (tier 0 is default) +local tile_tiers = { + ['stone-path'] = 1, + ['concrete'] = 2, + ['refined-concrete'] = 3, + ['hazard-concrete-right'] = 2, + ['hazard-concrete-left'] = 2, + ['refined-hazard-concrete-right'] = 3, + ['refined-hazard-concrete-left'] = 3 +} + +local tier_0 = { + 'transport-belt', + 'fast-transport-belt', + 'express-transport-belt', + 'underground-belt', + 'fast-underground-belt', + 'express-underground-belt', + 'small-electric-pole', + 'burner-mining-drill', + 'pumpjack', + 'car', + 'tank', + 'pipe', + 'pipe-to-ground', + 'offshore-pump', + 'locomotive', + 'cargo-wagon', + 'fluid-wagon', + 'artillery-wagon' +} + +--- Items explicitly allowed everywhere (tier 0 and up) +--- The RestrictEntities module auto skips all checks for these entities +--- They do not trigger set_keep_alive_callback or the on_pre_restricted_entity_destroyed event +RestrictEntities.add_allowed(tier_0) + +-- Items only allowed on tiles of tier 2 or higher (tier 1 is the default) +local entity_tiers = { + -- Tier 2 + ['oil-refinery'] = 2, + ['chemical-plant'] = 2, + ['storage-tank'] = 2, + ['rail'] = 2, + ['straight-rail'] = 2, + ['curved-rail'] = 2, + ['train-stop'] = 2, + ['solar-panel'] = 2, + ['flamethrower-turret'] = 2, + ['assembling-machine-2'] = 2, + ['steel-furnace'] = 2, + ['fast-inserter'] = 2, + ['filter-inserter'] = 2, + ['accumulator'] = 2, + ['big-electric-pole'] = 2, + -- Tier 3 + ['rocket-silo'] = 3, + ['nuclear-reactor'] = 3, + ['centrifuge'] = 3, + ['heat-exchanger'] = 3, + ['heat-pipe'] = 3, + ['steam-turbine'] = 3, + ['artillery-turret'] = 3, + ['roboport'] = 3, + ['beacon'] = 3, + ['assembling-machine-3'] = 3, + ['electric-furnace'] = 3, + ['substation'] = 3, + ['laser-turret'] = 3, + ['stack-inserter'] = 3, + ['stack-filter-inserter'] = 3, + ['logistic-chest-active-provider'] = 3, + ['logistic-chest-passive-provider'] = 3, + ['logistic-chest-buffer'] = 3, + ['logistic-chest-storage'] = 3, + ['logistic-chest-requester'] = 3 +} + +--Creates rich text icons of the tiered entities +local tier_0_items = '' +local tier_0_counter = 0 + +for _, v in pairs(tier_0) do + tier_0_items = tier_0_items .. ' [entity=' .. v .. ']' + tier_0_counter = tier_0_counter + 1 + if tier_0_counter > 10 then + tier_0_counter = 0 + tier_0_items = tier_0_items .. '\n' + end +end + +local tier_2_items = '' +local tier_3_items = '' + +local tier_2_counter = 0 +local tier_3_counter = 0 + +for k, v in pairs(entity_tiers) do + if not (k == 'rail') then + if (v == 3) then + tier_3_items = tier_3_items .. ' [entity=' .. k .. ']' + tier_3_counter = tier_3_counter + 1 + elseif (v == 2) then + tier_2_items = tier_2_items .. ' [entity=' .. k .. ']' + tier_2_counter = tier_2_counter + 1 + end + + if tier_3_counter > 14 then + tier_3_counter = 0 + tier_3_items = tier_3_items .. '\n' + elseif tier_2_counter > 14 then + tier_2_counter = 0 + tier_2_items = tier_2_items .. '\n' + end + end +end + +local tile_tiers_entities = '[font=default-bold]You may only build the factory on:[/font]\n' +local tile_tiers_entities_counter = 0 + +for k, _ in pairs(tile_tiers) do + tile_tiers_entities = tile_tiers_entities .. ' [tile=' .. k .. '] ' .. k + tile_tiers_entities_counter = tile_tiers_entities_counter + 1 + if tile_tiers_entities_counter == 3 or tile_tiers_entities_counter == 5 then + --tile_tiers_entities_counter = 0 + tile_tiers_entities = tile_tiers_entities .. '\n' + end +end + +ScenarioInfo.add_map_extra_info( + tile_tiers_entities .. + [[ + + +[font=default-bold]Exceptions:[/font] +]] .. + tier_0_items .. + [[ + + +Stone bricks provide ground support for most buildings/entities, +but some require better ground support! + +[font=default-bold]Ground support minimum concrete:[/font] +]] .. + tier_2_items .. [[ + + +[font=default-bold]Ground support minimum refined concrete:[/font] +]] .. tier_3_items .. [[ + + +Due to security measures you can not remove ground support nor downgrade it! +]] +) + +RestrictEntities.set_tile_bp() +RestrictEntities.enable_spill() + +--- The logic for checking that there are the correct ground support under the entity's position +RestrictEntities.set_keep_alive_callback( + Token.register( + function(entity) + if not (entity and entity.valid) then + return false + end + local get_tile = entity.surface.get_tile + local area = entity.bounding_box + local left_top = area.left_top + local right_bottom = area.right_bottom + local name = entity.name + + if name == 'entity-ghost' then + name = entity.ghost_name + end + + local entity_tier = entity_tiers[name] or 1 + + for x = floor(left_top.x), floor(right_bottom.x) do + for y = floor(left_top.y), floor(right_bottom.y) do + local tile_name = get_tile(x, y).name + local tile_tier = tile_tiers[tile_name] or 0 + + if entity_tier > tile_tier then + return false + end + end + end + return true + end + ) +) + +--- Anti griefing + +local anti_grief_tries = 3 -- Change this number to change how many 'shots' a player has got +local forgiveness_time = 432000 -- Time before previous offences are forgiven, in ticks (60 ticks/sec) +local anti_grief_kick_tries = math.ceil(anti_grief_tries / 2) -- Tries before kick warning + +RestrictEntities.set_anti_grief_callback( + Token.register( + function(_, player) + Popup.player(player, [[ +Look out! + +You are trying to replace entities which have items inside! +This is causing the items to spill out on the ground. + +Please be careful! +]], nil, nil, 'entity_placement_restriction_inventory_warning') + + if not (Rank.equal_or_greater_than(player.name, Ranks.auto_trusted)) then + local player_stat = times_spilled[player.index] + local number_of_spilled = player_stat and player_stat.count or 0 + + local last_offence = player_stat and player_stat.tick or 0 + local time_since_last_offence = game.tick - last_offence + + if (last_offence > 0 and time_since_last_offence >= forgiveness_time) then + number_of_spilled = 0 + end + + if number_of_spilled >= anti_grief_tries then + Report.jail(player, nil) + player.print({'', '[color=yellow]', {'concrete_jungle.anti_grief_jail_reason'}, '[/color]'}) + Report.report(nil, player, 'Spilling too many items on the ground') + times_spilled[player.index] = nil + return + elseif number_of_spilled == anti_grief_kick_tries then + game.kick_player(player, {'concrete_jungle.anti_grief_kick_reason'}) + end + times_spilled[player.index] = {count = number_of_spilled + 1, tick = game.tick} + end + end + ) +) + +local function print_floating_text(player, entity, text, color) + color = color or Color.white + local surface = player.surface + local position = entity.position + + return surface.create_entity { + name = 'tutorial-flying-text', + color = color, + text = text, + position = position, + render_player_index = player.index + } +end + +--- Warning for players when their entities are destroyed (needs to be pre because of the stack) +local function on_destroy(event) + local p = game.get_player(event.player_index) + local stack = event.stack + local entity = event.created_entity + local name + + if stack.valid_for_read then + name = stack.name + else + name = entity.name + end + + if p and p.valid then + if not (name == 'blueprint' or name == 'blueprint-book') then + if name == 'entity-ghost' then + name = entity.ghost_name + end + + local tier = {'item-name.stone-path'} + if (entity_tiers[name] == 2) then + tier = {'item-name.concrete'} + elseif (entity_tiers[name] == 3) then + tier = {'item-name.refined-concrete'} + end + local text = {'concrete_jungle.entity_not_enough_ground_support', '[item=' .. name .. ']', tier} + print_floating_text(p, entity, text) + else + p.print({'', '[color=yellow]', {'concrete_jungle.blueprint_not_enough_ground_support', {'', '[/color][color=red]', {'concrete_jungle.blueprint'}, '[/color][color=yellow]'}}, '[/color]'}) + end + end +end + +local function remove_tile_from_player(name, player) + if name == 'stone-path' then + name = 'stone-brick' + elseif name == 'hazard-concrete-left' or name == 'hazard-concrete-right' then + name = 'hazard-concrete' + elseif name == 'refined-hazard-concrete-left' or name == 'refined-hazard-concrete-right' then + name = 'refined-hazard-concrete' + end + player.remove_item({name = name}) +end + +local function player_mined_tile(event) + local player_index = event.player_index + local surface = game.surfaces[event.surface_index] + local oldTiles = event.tiles + local tiles = {} + + local player = game.get_player(player_index) + player.clean_cursor() + + for _, oldTile in pairs(oldTiles) do + local name = oldTile.old_tile.name + table.insert(tiles, {name = name, position = oldTile.position}) + remove_tile_from_player(name, player) + end + + surface.set_tiles(tiles, true) +end + +local function marked_for_deconstruction(event) + local entity = event.entity + + if entity.name == 'deconstructible-tile-proxy' then + if entity and entity.valid then + entity.destroy() + end + end +end + +local function player_built_tile(event) + local newTile = event.tile + local tier = tile_tiers[newTile.name] + + if (tier) then + local tiles = {} + local oldTiles = event.tiles + + local index = event.player_index + local robot = event.robot + local player + if index then + player = game.get_player(index) + else + player = robot + end + + local surface = game.surfaces[event.surface_index] + + for _, oldTile in pairs(oldTiles) do + local name = oldTile.old_tile.name + local tile_tier = tile_tiers[name] or 0 + if (tile_tier ~= 0 and tile_tier > tier) then + local newName = newTile.name + local position = oldTile.position + table.insert(tiles, {name = name, position = position}) + + remove_tile_from_player(name, player) + + if newName == 'stone-path' then + newName = 'stone-brick' + elseif newName == 'hazard-concrete-left' or name == 'hazard-concrete-right' then + newName = 'hazard-concrete' + elseif newName == 'refined-hazard-concrete-left' or name == 'refined-hazard-concrete-right' then + newName = 'refined-hazard-concrete' + end + local item = {name = newName} + if player.can_insert(item) then + player.insert(item) + else + player.surface.spill_item_stack(position, item, true, player.force, false) + end + end + end + surface.set_tiles(tiles, true) + end +end + +local first_time = + Token.register( + function(params) + local p = game.get_player(params.event.player_index) + Popup.player( + p, + { + '', + '[color=yellow]', + {'concrete_jungle.welcome_popup_player_name', p.name}, + '[/color]\n\n', + {'concrete_jungle.welcome_popup_line_1'}, + '\n\n', + {'concrete_jungle.welcome_popup_line_2'}, + '\n\n', + {'concrete_jungle.welcome_popup_line_3', '[color=red]Map Info[/color]', '[color=red]Redmew Info[/color]', '[virtual-signal=signal-info]'} + }, + {'concrete_jungle.welcome_popup_title'}, + nil, + 'concrete_jungle_intro' + ) + end +) + +Event.add(RestrictEntities.events.on_pre_restricted_entity_destroyed, on_destroy) +Event.add(defines.events.on_player_mined_tile, player_mined_tile) +Event.add(defines.events.on_marked_for_deconstruction, marked_for_deconstruction) +Event.add(defines.events.on_player_built_tile, player_built_tile) +Event.add(defines.events.on_robot_built_tile, player_built_tile) +Event.add( + defines.events.on_player_created, + function(event) + Task.set_timeout_in_ticks(900, first_time, {event = event}) + end +) +Event.on_init(on_init) + +--- Creating the starting circle (Map_gen) +local circle = b.circle(6) +local square = b.rectangle(3, 3) +local concrete_square = b.change_tile(square, true, 'hazard-concrete-left') +local stone_circle = b.change_tile(circle, true, 'stone-path') +stone_circle = b.if_else(concrete_square, stone_circle) + +local water_circle = b.circle(250) +local land_circle = b.circle(200) + +water_circle = b.subtract(water_circle, land_circle) + +local cross = b.add(b.line_x(4), b.line_y(4)) +cross = b.change_tile(cross, true, 'landfill') + +cross = b.subtract(cross, b.invert(water_circle)) + +water_circle = b.change_tile(water_circle, true, 'water') +water_circle = b.fish(water_circle, 0.0025) + +local map = b.if_else(water_circle, b.full_shape) + +map = b.if_else(cross, map) + +map = b.if_else(stone_circle, map) + +return map diff --git a/map_gen/maps/rail_grid/rail_grid_restrictions.lua b/map_gen/maps/rail_grid/rail_grid_restrictions.lua index 12c09a25..86e4f040 100644 --- a/map_gen/maps/rail_grid/rail_grid_restrictions.lua +++ b/map_gen/maps/rail_grid/rail_grid_restrictions.lua @@ -2,6 +2,7 @@ local Event = require 'utils.event' local Global = require 'utils.global' local RestrictEntities = require 'map_gen.shared.entity_placement_restriction' local Popup = require 'features.gui.popup' +local Token = require 'utils.token' local floor = math.floor @@ -37,20 +38,21 @@ local function all_on_landfill(entity) end RestrictEntities.set_keep_alive_callback( - function(entity) - local name = entity.name - if name == 'entity-ghost' then - name = entity.ghost_name - end + Token.register( + function(entity) + local name = entity.name + if name == 'entity-ghost' then + name = entity.ghost_name + end - if not rail_entities[name] then - return true - end + if not rail_entities[name] then + return true + end - return all_on_landfill(entity) - end + return all_on_landfill(entity) + end + ) ) - -- On first time player places rail entity on invalid tile, show popup explaining the rail mechanic. local function restricted_entity_destroyed(event) local p = event.player @@ -74,10 +76,7 @@ local function player_joined_game(event) return end - player.print( - "Welcome to RedMew's Rail Grids Map. Rails can only be built on green tiles.", - {r = 0, g = 1, b = 0, a = 1} - ) + player.print("Welcome to RedMew's Rail Grids Map. Rails can only be built on green tiles.", {r = 0, g = 1, b = 0, a = 1}) end Event.add(RestrictEntities.events.on_restricted_entity_destroyed, restricted_entity_destroyed) diff --git a/map_gen/shared/danger_ore_banned_entities.lua b/map_gen/shared/danger_ore_banned_entities.lua index e1aae2c9..f4752dde 100644 --- a/map_gen/shared/danger_ore_banned_entities.lua +++ b/map_gen/shared/danger_ore_banned_entities.lua @@ -1,6 +1,7 @@ -- This module prevents all but the allowed items from being built on top of resources local RestrictEntities = require 'map_gen.shared.entity_placement_restriction' local Event = require 'utils.event' +local Token = require 'utils.token' --- Items explicitly allowed on ores RestrictEntities.add_allowed( @@ -25,18 +26,20 @@ RestrictEntities.add_allowed( --- The logic for checking that there are resources under the entity's position RestrictEntities.set_keep_alive_callback( - function(entity) - -- Some entities have a bounding_box area of zero, eg robots. - local area = entity.bounding_box - local left_top, right_bottom = area.left_top, area.right_bottom - if left_top.x == right_bottom.x and left_top.y == right_bottom.y then - return true + Token.register( + function(entity) + -- Some entities have a bounding_box area of zero, eg robots. + local area = entity.bounding_box + local left_top, right_bottom = area.left_top, area.right_bottom + if left_top.x == right_bottom.x and left_top.y == right_bottom.y then + return true + end + local count = entity.surface.count_entities_filtered {area = area, type = 'resource', limit = 1} + if count == 0 then + return true + end end - local count = entity.surface.count_entities_filtered {area = area, type = 'resource', limit = 1} - if count == 0 then - return true - end - end + ) ) --- Warning for players when their entities are destroyed diff --git a/map_gen/shared/entity_placement_restriction.lua b/map_gen/shared/entity_placement_restriction.lua index 9d1faed4..a73c94b5 100644 --- a/map_gen/shared/entity_placement_restriction.lua +++ b/map_gen/shared/entity_placement_restriction.lua @@ -9,6 +9,8 @@ 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. + 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 Refunds for items that were placed can be toggled on or off via the enable and disable_refund functions @@ -24,16 +26,18 @@ -- The function provided does nothing but return nil -- every entity will be destroyed except those on the allowed list RestrictEntities.add_allowed({'transport-belt'}) - RestrictEntities.set_keep_alive_callback(function() end) + RestrictEntities.set_keep_alive_callback(Token.register(function() end)) -- 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( - function(entity) - if entity.surface.count_entities_filtered {area = entity.bounding_box, type = 'resource', limit = 1} == 0 then - return true + Token.register( + function(entity) + if entity.surface.count_entities_filtered {area = entity.bounding_box, type = 'resource', limit = 1} == 0 then + return true + end end - end + ) ) ]] local Event = require 'utils.event' @@ -80,7 +84,10 @@ local banned_entities = {} local primitives = { event = nil, -- if the event is registered or not refund = true, -- if we issue a refund or not - keep_alive_callback = nil -- the function to process entities through + prevent_tile_bp = false, -- prevents players from placing blueprints with tiles + 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 } Global.register( @@ -98,6 +105,72 @@ Global.register( -- Local functions +--- Spill items stacks +-- @param entity the entity from which the items should be spilled +-- @param item the item stack that should be spilled +local function spill_item_stack(entity, item) + entity.surface.spill_item_stack(entity.position, item, true, entity.force, false) +end + +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 +local delay_clean_cursor = + Token.register( + function(param) + param.player.clean_cursor() + end +) + +--- 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 + Task.set_timeout_in_ticks(1, delay_clean_cursor, {player = player}) + local type = entity.type + if type == 'container' then + for item, count in pairs(entity.get_inventory(defines.inventory.chest).get_contents()) do + spill_item_stack(entity, {name = item, count = count}) + end + elseif type == 'logistic-container' then + entity.surface.create_entity {name = 'steel-chest', position = entity.position, direction = entity.direction, force = entity.force, fast_replace = true, spill = false} + if player and player.valid and primitives.refund then -- refunding materials required to make a logistic container minus the "free" steel-chest generated above + player.insert({name = 'electronic-circuit', count = 3}) + player.insert({name = 'advanced-circuit', count = 1}) + end + return true + 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 + 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 + end + + Token.get(primitives.anti_grief_callback)(entity, player) + end + return false +end + --- Token for the on_built event callback, checks if an entity should be destroyed. local on_built_token = Token.register( @@ -109,6 +182,9 @@ local on_built_token = local name = entity.name if name == 'tile-ghost' then + if primitives.prevent_tile_bp and entity.ghost_name ~= 'landfill' then + entity.destroy() + end return end @@ -132,7 +208,7 @@ local on_built_token = -- destroy in these cases: -- all banned ents -- not banned and callback function and not saved by callback - if not banned_entities[name] and (not keep_alive_callback or keep_alive_callback(entity)) then + if not banned_entities[name] and (not keep_alive_callback or Token.get(keep_alive_callback)(entity)) then return end @@ -149,14 +225,20 @@ local on_built_token = } ) + local player = game.get_player(index) + -- Need to revalidate the entity since we sent it to the raised event if entity.valid then - entity.destroy() + -- Checking if the entity has an inventory and spills the content on the ground to prevent destroying those too + if entities_with_inventory(entity, player) then + ghost = true -- Cheating to prevent refunds + else + entity.destroy() + end end -- 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 - local player = game.get_player(index) local item_returned if player and player.valid and primitives.refund and not ghost and stack.valid then player.insert(stack) @@ -201,8 +283,8 @@ end -- logic on what entities should and should not be destroyed. -- @param keep_alive_callback function Public.set_keep_alive_callback(keep_alive_callback) - if type(keep_alive_callback) ~= 'function' then - error('Sending a non-function') + if type(keep_alive_callback) ~= 'number' then + error('Sending a non-token function') end primitives.keep_alive_callback = keep_alive_callback check_event_status() @@ -214,6 +296,21 @@ function Public.remove_keep_alive_callback() check_event_status() end +--- 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 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 + --- Adds to the list of allowed entities -- @param ents array of string entity names function Public.add_allowed(ents) @@ -272,4 +369,24 @@ function Public.set_refund() primitives.refund = false end +--- Enables the ability to blueprint tiles (Landfill always enabled) +function Public.enable_tile_bp() + primitives.prevent_tile_bp = false +end + +--- Disables the ability to blueprint tiles (Landfill always enabled) +function Public.set_tile_bp() + primitives.prevent_tile_bp = true +end + +--- Enables the spill function +function Public.enable_spill() + primitives.spill = true +end + +--- Disables the spill function +function Public.set_spill() + primitives.spill = false +end + return Public