1
0
mirror of https://github.com/Refactorio/RedMew.git synced 2025-03-11 14:49:59 +02:00

Refactor corpse features:

- Turn dump_offline_inventories on by default.
- Reduce corpse_util to storing and removing corpse tags.
- Add death_corpse_tags which handles adding tags for dead players. This prevents dump_offline_inventories from needing a dependency on what is now death_corpse_tags.
- If a player dies with no items, don't create a map tag and remove the corpse.
- Changed the message slightly for dump_offline_inventories to make it consistent with the other corpse messages.
This commit is contained in:
James Gillham 2021-03-08 20:25:15 +00:00
parent 132ba7627c
commit ac78f22277
7 changed files with 206 additions and 114 deletions

View File

@ -81,10 +81,9 @@ global.config = {
tag_group = {
enabled = true
},
-- enables dumping of inventories of offline players to a corpse near spawn
-- This feature is dependant upon corpse_util and will enable it
-- enables dumping of inventories of offline players to a corpse at the player's last location
dump_offline_inventories = {
enabled = false,
enabled = true,
offline_timout_mins = 15, -- time after which a player logs off that their inventory is provided to the team
},
-- enables players to create and prioritize tasks
@ -287,7 +286,7 @@ global.config = {
enabled = true
},
-- when a player dies, leaves a map marker until the corpse expires or is looted
corpse_util = {
death_corpse_tags = {
enabled = true
},
-- adds many commands for users and admins alike

View File

@ -49,8 +49,8 @@ end
if config.hodor.enabled or config.auto_respond.enabled or config.mentions.enabled then
require 'features.chat_triggers'
end
if config.corpse_util.enabled then
require 'features.corpse_util'
if config.death_corpse_tags.enabled then
require 'features.death_corpse_tags'
end
if config.dump_offline_inventories.enabled then
require 'features.dump_offline_inventories'

View File

@ -4,96 +4,33 @@ local Task = require 'utils.task'
local Token = require 'utils.token'
local table = require 'utils.table'
local Utils = require 'utils.core'
local Settings = require 'utils.redmew_settings'
local Public = {}
local ping_own_death_name = 'corpse_util.ping_own_death'
local ping_other_death_name = 'corpse_util.ping_other_death'
Public.ping_own_death_name = ping_own_death_name
Public.ping_other_death_name = ping_other_death_name
Settings.register(ping_own_death_name, Settings.types.boolean, true, 'corpse_util.ping_own_death')
Settings.register(ping_other_death_name, Settings.types.boolean, false, 'corpse_util.ping_other_death')
local player_corpses = {}
Global.register(player_corpses, function(tbl)
player_corpses = tbl
end)
local function add_tag(tag, player_index, death_tick)
player_corpses[player_index * 0x100000000 + death_tick] = tag
local function get_index(player_index, tick)
return player_index * 0x100000000 + tick
end
local function player_died(event)
local player_index = event.player_index
local player = game.get_player(player_index)
if not player or not player.valid then
return
end
local pos = player.position
local entities = player.surface.find_entities_filtered {
area = {{pos.x - 0.5, pos.y - 0.5}, {pos.x + 0.5, pos.y + 0.5}},
name = 'character-corpse'
}
local tick = game.tick
local entity
for _, e in ipairs(entities) do
if e.character_corpse_player_index == event.player_index and e.character_corpse_tick_of_death == tick then
entity = e
break
end
end
if not entity or not entity.valid then
return
end
local text = player.name .. "'s corpse"
local position = entity.position
local tag = player.force.add_chart_tag(player.surface, {
icon = {type = 'item', name = 'power-armor-mk2'},
position = position,
text = text
})
if not tag then
return
end
if Settings.get(player_index, ping_own_death_name) then
player.print({
'corpse_util.own_corpse_location',
string.format('%.1f', position.x),
string.format('%.1f', position.y),
player.surface.name
})
end
for _, other_player in pairs(player.force.players) do
if other_player ~= player and Settings.get(other_player.index, ping_other_death_name) then
other_player.print({
'corpse_util.other_corpse_location',
player.name,
string.format('%.1f', position.x),
string.format('%.1f', position.y),
player.surface.name
})
end
end
add_tag(tag, player_index, tick)
local function get_data(player_index, tick)
local index = get_index(player_index, tick)
return player_corpses[index]
end
local function remove_tag(player_index, tick)
local index = player_index * 0x100000000 + tick
local index = get_index(player_index, tick)
local tag = player_corpses[index]
local data = player_corpses[index]
if not data then
return
end
local tag = data.tag
player_corpses[index] = nil
if not tag or not tag.valid then
@ -126,18 +63,24 @@ local function mined_entity(event)
return
end
local corpse_owner_index = entity.character_corpse_player_index
local death_tick = entity.character_corpse_tick_of_death
-- The corpse may be mined but not removed (if player doesn't have inventory space)
-- so we wait one tick to see if the corpse is gone.
Task.set_timeout_in_ticks(1, corpse_util_mined_entity, {
entity = entity,
player_index = entity.character_corpse_player_index,
tick = entity.character_corpse_tick_of_death
player_index = corpse_owner_index,
tick = death_tick
})
local player_index = event.player_index
local corpse_owner_index = entity.character_corpse_player_index
if player_index == corpse_owner_index then
return
end
if player_index == corpse_owner_index or not entity.active then
local data = get_data(corpse_owner_index, death_tick)
if not data or not data.alert_looting then
return
end
@ -159,7 +102,13 @@ local function on_gui_opened(event)
local player_index = event.player_index
local corpse_owner_index = entity.character_corpse_player_index
if player_index == corpse_owner_index or not entity.active then
if player_index == corpse_owner_index then
return
end
local death_tick = entity.character_corpse_tick_of_death
local data = get_data(corpse_owner_index, death_tick)
if not data or not data.alert_looting then
return
end
@ -194,7 +143,6 @@ local function on_gui_closed(event)
end
end
Event.add(defines.events.on_player_died, player_died)
Event.add(defines.events.on_character_corpse_expired, corpse_expired)
Event.add(defines.events.on_pre_player_mined_item, mined_entity)
Event.add(defines.events.on_gui_opened, on_gui_opened)
@ -204,9 +152,9 @@ function Public.clear()
table.clear_table(player_corpses)
end
Public.add_tag = add_tag
Public._player_died = player_died
Public.player_corpses = player_corpses
function Public.add_tag(tag, player_index, death_tick, alert_looting)
local index = get_index(player_index, death_tick)
player_corpses[index] = {tag = tag, alert_looting = alert_looting}
end
return Public

View File

@ -0,0 +1,94 @@
local Event = require 'utils.event'
local Settings = require 'utils.redmew_settings'
local CorpseUtil = require 'features.corpse_util'
local Public = {}
local ping_own_death_name = 'death_corpse_tags.ping_own_death'
local ping_other_death_name = 'death_corpse_tags.ping_other_death'
Public.ping_own_death_name = ping_own_death_name
Public.ping_other_death_name = ping_other_death_name
Settings.register(ping_own_death_name, Settings.types.boolean, true, 'death_corpse_tags.ping_own_death')
Settings.register(ping_other_death_name, Settings.types.boolean, false, 'death_corpse_tags.ping_other_death')
local function player_died(event)
local player_index = event.player_index
local player = game.get_player(player_index)
if not player or not player.valid then
return
end
local pos = player.position
local entities = player.surface.find_entities_filtered {
area = {{pos.x - 0.5, pos.y - 0.5}, {pos.x + 0.5, pos.y + 0.5}},
name = 'character-corpse'
}
local tick = game.tick
local entity
for _, e in ipairs(entities) do
if e.character_corpse_player_index == event.player_index and e.character_corpse_tick_of_death == tick then
entity = e
break
end
end
if not entity or not entity.valid then
return
end
local inv_corpse = entity.get_inventory(defines.inventory.character_corpse)
if not inv_corpse or not inv_corpse.valid then
return
end
if inv_corpse.is_empty() then
entity.destroy()
player.print({'death_corpse_tags.empty_corpse'})
return
end
local text = player.name .. "'s corpse"
local position = entity.position
local tag = player.force.add_chart_tag(player.surface, {
icon = {type = 'item', name = 'power-armor-mk2'},
position = position,
text = text
})
if not tag then
return
end
if Settings.get(player_index, ping_own_death_name) then
player.print({
'death_corpse_tags.own_corpse_location',
string.format('%.1f', position.x),
string.format('%.1f', position.y),
player.surface.name
})
end
for _, other_player in pairs(player.force.players) do
if other_player ~= player and Settings.get(other_player.index, ping_other_death_name) then
other_player.print({
'death_corpse_tags.other_corpse_location',
player.name,
string.format('%.1f', position.x),
string.format('%.1f', position.y),
player.surface.name
})
end
end
CorpseUtil.add_tag(tag, player_index, tick, true)
end
Event.add(defines.events.on_player_died, player_died)
Public._player_died = player_died
return Public

View File

@ -4,6 +4,7 @@ local Assert = require 'utils.test.assert'
local Helper = require 'utils.test.helper'
local Settings = require 'utils.redmew_settings'
local CorpseUtil = require 'features.corpse_util'
local DeathCorpseTags = require 'features.death_corpse_tags'
local function test_teardown(context)
context:add_teardown(CorpseUtil.clear)
@ -18,7 +19,7 @@ local function declare_test(name, func)
Declare.test(name, test_func)
end
Declare.module({'features', 'corpse_util'}, function()
Declare.module({'features', 'death_corpse_tags'}, function()
local teardown
Declare.module_startup(function(context)
@ -46,20 +47,26 @@ Declare.module({'features', 'corpse_util'}, function()
end)
end
local function fake_death(player)
local function fake_death(player, has_items)
local surface = player.surface
local position = player.position
local entity = surface.create_entity {
name = 'character-corpse',
position = position,
player_index = player.index
player_index = player.index,
inventory_size = has_items and 1 or nil
}
if not entity or not entity.valid then
error('no corpse')
end
if has_items then
local inventory = entity.get_inventory(defines.inventory.character_corpse)
inventory.insert('iron-plate')
end
return EventFactory.on_player_died(player.index)
end
@ -77,13 +84,13 @@ Declare.module({'features', 'corpse_util'}, function()
return player
end)
local event = fake_death(player)
local event = fake_death(player, true)
-- Act.
CorpseUtil._player_died(event)
DeathCorpseTags._player_died(event)
-- Assert.
local expected = {'corpse_util.own_corpse_location', '0.0', '0.0', player.surface.name}
local expected = {'death_corpse_tags.own_corpse_location', '0.0', '0.0', player.surface.name}
Assert.table_equal(expected, actual_text)
end)
@ -105,7 +112,7 @@ Declare.module({'features', 'corpse_util'}, function()
position = EventFactory.position({1, 1})
}
change_settings_for_test(context, CorpseUtil.ping_other_death_name, true)
change_settings_for_test(context, DeathCorpseTags.ping_other_death_name, true)
Helper.modify_lua_object(context, game, 'get_player', function(index)
if index == player.index then
@ -123,20 +130,20 @@ Declare.module({'features', 'corpse_util'}, function()
actual_text = text
end)
local event = fake_death(second_player)
local event = fake_death(second_player, true)
-- Act.
CorpseUtil._player_died(event)
DeathCorpseTags._player_died(event)
-- Assert.
local expected = {'corpse_util.other_corpse_location', second_player.name, '1.0', '1.0', player.surface.name}
local expected = {'death_corpse_tags.other_corpse_location', second_player.name, '1.0', '1.0', player.surface.name}
Assert.table_equal(expected, actual_text)
end)
declare_test('do not ping player corpse location when died and setting disabled', function(context)
-- Arrange.
local player = context.player
change_settings_for_test(context, CorpseUtil.ping_own_death_name, false)
change_settings_for_test(context, DeathCorpseTags.ping_own_death_name, false)
local actual_text
@ -148,10 +155,10 @@ Declare.module({'features', 'corpse_util'}, function()
return player
end)
local event = fake_death(player)
local event = fake_death(player, true)
-- Act.
CorpseUtil._player_died(event)
DeathCorpseTags._player_died(event)
-- Assert.
Assert.is_nil(actual_text)
@ -176,7 +183,7 @@ Declare.module({'features', 'corpse_util'}, function()
position = EventFactory.position({1, 1})
}
change_settings_for_test(context, CorpseUtil.ping_other_death_name, false)
change_settings_for_test(context, DeathCorpseTags.ping_other_death_name, false)
Helper.modify_lua_object(context, game, 'get_player', function(index)
if index == player.index then
@ -194,10 +201,10 @@ Declare.module({'features', 'corpse_util'}, function()
actual_text = text
end)
local event = fake_death(second_player)
local event = fake_death(second_player, true)
-- Act.
CorpseUtil._player_died(event)
DeathCorpseTags._player_died(event)
-- Assert.
Assert.is_nil(actual_text)
@ -210,8 +217,8 @@ Declare.module({'features', 'corpse_util'}, function()
local actual_text
change_settings_for_test(context, CorpseUtil.ping_own_death_name, false)
change_settings_for_test(context, CorpseUtil.ping_other_death_name, true)
change_settings_for_test(context, DeathCorpseTags.ping_own_death_name, false)
change_settings_for_test(context, DeathCorpseTags.ping_other_death_name, true)
Helper.modify_lua_object(context, game, 'get_player', function()
return player
@ -224,12 +231,44 @@ Declare.module({'features', 'corpse_util'}, function()
actual_text = text
end)
local event = fake_death(player)
local event = fake_death(player, true)
-- Act.
CorpseUtil._player_died(event)
DeathCorpseTags._player_died(event)
-- Assert.
Assert.is_nil(actual_text)
end)
declare_test('corpse removed and empty message when corpse is empty', function(context)
-- Arrange.
local player = context.player
player.teleport({5, 5})
context:add_teardown(function()
player.teleport({0, 0})
end)
local actual_text
Helper.modify_lua_object(context, player, 'print', function(text)
actual_text = text
end)
Helper.modify_lua_object(context, game, 'get_player', function()
return player
end)
local event = fake_death(player, false)
-- Act.
DeathCorpseTags._player_died(event)
-- Assert.
local corpses = player.surface.find_entities_filtered({name = 'character-corpse', position = player.position, radius = 1})
Assert.equal(0, #corpses)
local expected = {'death_corpse_tags.empty_corpse'}
Assert.table_equal(expected, actual_text)
end)
end)

View File

@ -72,10 +72,18 @@ local spawn_player_corpse =
text = text
})
game.print("[gps="..position.x..","..position.y..",redmew] "..player.name.." has been offline "..offline_timout_mins.." minutes. Their inventory is now available.")
local message = {
'dump_offline_inventories.inventory_location',
player.name,
offline_timout_mins,
string.format('%.1f', position.x),
string.format('%.1f', position.y),
player.surface.name
}
game.print(message)
if tag then
CorpseUtil.add_tag(tag, player_index, game.tick)
CorpseUtil.add_tag(tag, player_index, game.tick, false)
end
end
)

View File

@ -181,8 +181,12 @@ select_brush=Select Brush Tile.
instructions=Select a brush tile to replace [item=refined-concrete] and [item=refined-hazard-concrete].\nOnly works when Paint Brush window is open.
no_place_landfill=Coloured concrete can not be placed on landfill tiles.
[corpse_util]
[death_corpse_tags]
ping_own_death=Ping the location when you die.
ping_other_death=Ping the location when other players die.
own_corpse_location=[color=red][Corpse][/color] Your corpse is located at [gps=__1__,__2__,__3__]
other_corpse_location=[color=red][Corpse][/color] __1__'s corpse is located at [gps=__2__,__3__,__4__]
empty_corpse=[color=red][Corpse][/color]Your corpse was empty and has been removed.
[dump_offline_inventories]
inventory_location=[color=blue][Corpse][/color] __1__ has been offline __2__ minutes. Their inventory is now available at [gps=__3__,__4__,__5__]