mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-11-12 15:54:52 +09:00
Review points from Cooldude: - Feature.register(name[, config]) is required again and returns a feature object. Each feature has its config, its fields, and its overrides, so one registry holds all the data. Only the overrides are stored. On load each feature points at its own stored table again. - The methods are feature:guard(lib), feature:update_config(enabled, values), and feature:is_enabled(). Feature.get(name) looks up a feature, and commands flag the feature object. - update_config raises on_config_updated once per changed value, with the feature name, path, old value, and new value. The gui refresh only reacts to the "enabled" path. - apply only sets values. on_apply is gone. Lookup sets built from lists use Feature.to_set, which is cached by the list itself. A replaced list gets a new set, so the sets are also correct after a load. Protection exposes getters for its protected names and types. Web: settings use small inputs, the same size as the config pages. Fields are shaped like clusterio config field definitions. A field with an inputComponent renders the component registered with the web interface, so the role setting now uses core's role input, and other plugin inputs can be used later. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
162 lines
5.6 KiB
Lua
162 lines
5.6 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 = Feature.register("death_markers", {
|
|
collect_corpses = true, --- @setting collect_corpses enables items being returned to the spawn point in chests upon corpse expiring
|
|
show_map_markers = true, --- @setting show_map_markers shows markers on the map where bodies are
|
|
clean_map_markers = false, --- @setting clean_map_markers removes the map marker once the body is gone
|
|
include_time_of_death = true, --- @setting include_time_of_death weather to include the time of death on the map marker
|
|
map_icon = nil, --- @setting map_icon the icon that the map marker shows; nil means no icon; format as a SingleID
|
|
show_light_at_corpse = true, --- @setting show_light_at_corpse if a light should be rendered at the corpse
|
|
show_line_to_corpse = true, --- @setting show_line_to_corpse if a line should be rendered from you to your corpse
|
|
})
|
|
local config = feature.config
|
|
|
|
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,
|
|
},
|
|
}
|