mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-12 07:54:52 +09:00
- Feature.register returns the config as a second value, so files write `local feature, config = Feature.register(...)`. - The lib is ExpScenario.Features and the instance class with its fields sits on Feature._prototype, which is the __index, the same shape as exp_commands. - update_config only looks at values whose override was added, removed, or replaced. _apply_field sets one value and returns it, so the comparison happens while applying and the second loop only raises. Overrides dropped from the previous set count as candidates because the value reverts to its default. - The `@setting name` tags are gone from the config comments. - Protection is guarded as a whole instead of checking config.enabled in one handler. The handlers also forget mined entities, so get_entities and is_entity_protected now drop invalid references, which also covers entities destroyed without an event. The list getters are gone, the selection overlay builds its lists from Protection.config. - Chat and damage popups are one feature in control/popups.lua. - ignore_admins and command_admin_only are removed, roles cover them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
161 lines
5.4 KiB
Lua
161 lines
5.4 KiB
Lua
--[[-- Control - Death Markers
|
|
Makes markers on the map where places have died and reclaims items if not recovered
|
|
]]
|
|
|
|
local ExpUtil = require("modules/exp_util")
|
|
local Feature = require("modules/exp_scenario/features")
|
|
local Storage = require("modules/exp_util/storage")
|
|
|
|
local feature, config = Feature.register("death_markers", {
|
|
collect_corpses = true, -- enables items being returned to the spawn point in chests upon corpse expiring
|
|
show_map_markers = true, -- shows markers on the map where bodies are
|
|
clean_map_markers = false, -- removes the map marker once the body is gone
|
|
include_time_of_death = true, -- weather to include the time of death on the map marker
|
|
map_icon = nil, -- the icon that the map marker shows; nil means no icon; format as a SingleID
|
|
show_light_at_corpse = true, -- if a light should be rendered at the corpse
|
|
show_line_to_corpse = true, -- if a line should be rendered from you to your corpse
|
|
})
|
|
|
|
local map_tag_time_format = ExpUtil.format_time_factory{ format = "short", hours = true, minutes = true }
|
|
|
|
--- @class CorpseData
|
|
--- @field player LuaPlayer
|
|
--- @field corpse LuaEntity
|
|
--- @field tag LuaCustomChartTag?
|
|
--- @field created_at number
|
|
|
|
--- @type table<number, CorpseData>
|
|
local character_corpses = {}
|
|
Storage.register(character_corpses, function(tbl)
|
|
character_corpses = tbl
|
|
end)
|
|
|
|
--- Creates a new death marker and saves it to the given death
|
|
--- @param corpse_data CorpseData
|
|
local function create_map_tag(corpse_data)
|
|
local player = corpse_data.player
|
|
local message = player.name .. " died"
|
|
|
|
if config.include_time_of_death then
|
|
local time = map_tag_time_format(corpse_data.created_at)
|
|
message = message .. " at " .. time
|
|
end
|
|
|
|
corpse_data.tag = (player.force --[[@as LuaForce]]).add_chart_tag(corpse_data.corpse.surface, {
|
|
position = corpse_data.corpse.position,
|
|
icon = config.map_icon,
|
|
text = message,
|
|
})
|
|
end
|
|
|
|
--- Checks that all map tags are present and valid, creating any that are missing
|
|
local function check_map_tags()
|
|
if not config.show_map_markers then return end
|
|
for _, corpse_data in pairs(character_corpses) do
|
|
if not corpse_data.tag or not corpse_data.tag.valid then
|
|
create_map_tag(corpse_data)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- when a player dies a new death is added to the records and a map marker is made
|
|
--- @param event EventData.on_player_died
|
|
local function on_player_died(event)
|
|
local player = assert(game.get_player(event.player_index))
|
|
local corpse = player.surface.find_entity("character-corpse", player.physical_position)
|
|
if not corpse or not corpse.valid then return end
|
|
|
|
local corpse_data = {
|
|
player = player,
|
|
corpse = corpse,
|
|
created_at = event.tick,
|
|
}
|
|
|
|
local registration_number = script.register_on_object_destroyed(corpse)
|
|
character_corpses[registration_number] = corpse_data
|
|
|
|
-- Create a map marker
|
|
if config.show_map_markers then
|
|
create_map_tag(corpse_data)
|
|
end
|
|
|
|
-- Draw a light attached to the corpse with the player color
|
|
if config.show_light_at_corpse then
|
|
rendering.draw_light{
|
|
sprite = "utility/light_medium",
|
|
surface = player.surface,
|
|
color = player.color,
|
|
force = player.force,
|
|
target = corpse,
|
|
}
|
|
end
|
|
end
|
|
|
|
--- Called to remove stale corpse data
|
|
--- @param event EventData.on_object_destroyed
|
|
local function on_object_destroyed(event)
|
|
local corpse_data = character_corpses[event.registration_number]
|
|
character_corpses[event.registration_number] = nil
|
|
if not corpse_data then
|
|
return
|
|
end
|
|
|
|
local tag = corpse_data.tag
|
|
if tag and config.clean_map_markers then
|
|
tag.destroy()
|
|
end
|
|
end
|
|
|
|
--- Draw lines to the player corpse
|
|
--- @param event EventData.on_player_respawned
|
|
local function on_player_respawned(event)
|
|
if not config.show_line_to_corpse then return end
|
|
local index = event.player_index
|
|
local player = assert(game.get_player(index))
|
|
for _, corpse_data in pairs(character_corpses) do
|
|
if corpse_data.player.index == index then
|
|
local line_color = player.color
|
|
line_color.a = .3
|
|
rendering.draw_line{
|
|
color = line_color,
|
|
from = player.character,
|
|
to = corpse_data.corpse,
|
|
surface = player.surface,
|
|
players = { index },
|
|
draw_on_ground = true,
|
|
dash_length = 1,
|
|
gap_length = 1,
|
|
width = 2,
|
|
}
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Collect all items from expired character corpses
|
|
--- @param event EventData.on_character_corpse_expired
|
|
local function on_character_corpse_expired(event)
|
|
if not config.collect_corpses then return end
|
|
local corpse = event.corpse
|
|
local inventory = assert(corpse.get_inventory(defines.inventory.character_corpse))
|
|
ExpUtil.transfer_inventory_to_surface{
|
|
inventory = inventory,
|
|
surface = corpse.surface,
|
|
name = "iron-chest",
|
|
allow_creation = true,
|
|
}
|
|
end
|
|
|
|
local e = defines.events
|
|
|
|
return feature:guard{
|
|
on_nth_tick = {
|
|
[60 * 60 * 5] = check_map_tags,
|
|
},
|
|
events = {
|
|
[e.on_player_died] = on_player_died,
|
|
[e.on_object_destroyed] = on_object_destroyed,
|
|
[e.on_player_respawned] = on_player_respawned,
|
|
[e.on_character_corpse_expired] = on_character_corpse_expired,
|
|
},
|
|
}
|