Files
factorio-scenario-ExpCluster/exp_scenario/module/control/fast_deconstruction.lua
Cooldude2606 9bd699ebf1 Refactor legacy addons into Clusterio format (#413)
* Refactor custom start

* Refactor afk kick

* Fix use of assert get player

* Refactor chat popup

* Refactor chat auto reply

* Refactor help bubbles

* Refactor damage popups

* Refactor death markers

* Refactor deconstruction log

* Remove FAGC logging

* Refactor discord alerts

* Refactor insert pickup

* Refactor inventory clear

* Refactor extra logging

* Refactor nuke protection

* Refactor pollution grading

* Refactor protection jail

* Refactor report jail

* Refactor mine depletion

* Refactor degrading tiles

* Refactor station auto name

* Refactor spawn area

* Refactor fast deconstruction

* Bug Fixes
2025-12-02 18:34:24 +00:00

182 lines
5.6 KiB
Lua

--[[-- Control - Fast Deconstruction
Makes trees which are marked for decon "decay" quickly to allow faster building
]]
local Gui = require("modules/exp_gui")
local Async = require("modules/exp_util/async")
local Roles = require("modules.exp_legacy.expcore.roles")
local PlayerData = require("modules.exp_legacy.expcore.player_data")
local HasEnabledDecon = PlayerData.Settings:combine("HasEnabledDecon")
HasEnabledDecon:set_default(false)
local random = math.random
local floor = math.floor
local min = math.min
--- @class TreeDeconCache
--- @field tick number The tick this cache is valid
--- @field player_index number
--- @field player LuaPlayer
--- @field force LuaForce
--- @field trees LuaEntity[]
--- @field tree_count number
--- @field permission "fast" | "allow" | "disallow"
--- @field task Async.AsyncReturn
local cache --- @type TreeDeconCache?
local remove_trees_async =
Async.register(function(task_data)
--- @cast task_data TreeDeconCache
if task_data.tree_count == 0 then
return Async.status.complete()
end
local head = task_data.tree_count
local trees = task_data.trees
local max_remove = floor(head / 100) + 1
local remove_count = min(random(0, max_remove), head)
for i = 1, remove_count do
local index = random(1, head)
local entity = trees[index]
trees[index] = trees[head]
head = head - 1
if entity and entity.valid then
entity.destroy()
end
end
task_data.tree_count = head
return Async.status.continue(task_data)
end)
--- Check the permission the player has
--- @param player LuaPlayer
--- @return "fast" | "allow" | "disallow"
local function get_permission(player)
if Roles.player_allowed(player, "fast-tree-decon") then
return HasEnabledDecon:get(player) and "fast" or "allow"
elseif Roles.player_allowed(player, "standard-decon") then
return "allow"
else
return "disallow"
end
end
--- Return or build the cache for a player
--- @param player_index number
--- @return TreeDeconCache
local function get_player_cache(player_index)
-- Return the current cache if it is valid
if cache and cache.tick == game.tick and cache.player_index == player_index then
return cache
end
-- Create a new cache if the previous on is in use
if not cache or cache.task and not cache.task.completed then
cache = {} --[[@as any]]
end
local player = assert(game.get_player(player_index))
cache.tick = game.tick
cache.player_index = player_index
cache.player = player
cache.force = player.force --[[ @as LuaForce ]]
cache.tree_count = 0
cache.trees = {}
cache.permission = get_permission(player)
cache.task = remove_trees_async:start_soon(cache)
return cache
end
-- Left menu button to toggle between fast decon and normal decon marking
Gui.toolbar.create_button{
name = "toggle-tree-decon",
sprite = "entity/tree-01",
tooltip = { "exp_fast-decon.tooltip-main" },
auto_toggle = true,
visible = function(player, _)
return Roles.player_allowed(player, "fast-tree-decon")
end
}:on_click(function(def, player, element)
local state = Gui.toolbar.get_button_toggled_state(def, player)
HasEnabledDecon:set(player, state)
player.print{ "exp_fast-decon.chat-toggle", state and { "exp_fast-decon.chat-enabled" } or { "exp_fast-decon.chat-disabled" } }
end)
-- Add trees to queue when marked, only allows simple entities and for players with role permission
--- @param event EventData.on_marked_for_deconstruction
local function on_marked_for_deconstruction(event)
-- Check player and entity are valid
local entity = event.entity
local player_index = event.player_index
if not player_index or not entity.valid then
return
end
-- If it has a last user then either do nothing or cancel decon
local last_user = entity.last_user
local player_cache = get_player_cache(player_index)
if last_user then
if player_cache.permission == "disallow" then
entity.cancel_deconstruction(player_cache.force)
end
return
end
-- Allow fast decon on no last user and not cliff
if player_cache.permission == "fast" and entity.type ~= "cliff" then
local head = player_cache.tree_count + 1
player_cache.tree_count = head
player_cache.trees[head] = entity
end
end
--- Clear trees when hit with a car
--- @param event EventData.on_entity_damaged
local function on_entity_damaged(event)
-- Check it was an impact from a force
if not (event.damage_type.name == "impact" and event.force) then
return
end
-- Check the entity hit was a tree or rock
if not (event.entity.type == "tree" or event.entity.type == "simple-entity") then
return
end
-- Check the case was a car
if (not event.cause) or (event.cause.type ~= "car") then
return
end
-- Get a valid player as the driver
local driver = event.cause.get_driver()
if not driver then return end
if driver.object_name ~= "LuaPlayer" then
driver = driver.player
if not driver then return end
end
-- Mark the entity to be removed
local allow = get_player_cache(driver.index)
if allow == "fast" and HasEnabledDecon:get(driver) then
event.entity.destroy()
else
event.entity.order_deconstruction(event.force, driver)
end
end
local e = defines.events
return {
events = {
[e.on_entity_damaged] = on_entity_damaged,
[e.on_marked_for_deconstruction] = on_marked_for_deconstruction,
}
}