i fixed and merge all file into one
if remote.interfaces["keep-inventory"] then return end
---@type table<string, table>
local mod_data
---@type table<integer, LuaEntity>
local corpses_queue
---@type table<integer, table<string, integer>>
local corpse_inventory
-- LuaSurface, MapPosition
---@type table<integer, table>
local corpse_pos_data
-- Event handlers can still run when another part of the mod replaces one of
-- this module's lifecycle handlers. Rebind and initialize the persistent
-- tables at the point of use so the local aliases are always available.
local function ensure_data()
storage.keep_inventory = storage.keep_inventory or {}
local data = storage.keep_inventory
data.corpses_queue = data.corpses_queue or {}
data.corpse_inventory = data.corpse_inventory or {}
data.corpse_pos_data = data.corpse_pos_data or {}
mod_data = data
corpses_queue = data.corpses_queue
corpse_inventory = data.corpse_inventory
corpse_pos_data = data.corpse_pos_data
return data
end
remote.add_interface("keep-inventory", {
getSource = function()
local mod_name = script.mod_name
rcon.print(mod_name) -- Returns "level" if it's a scenario, otherwise "keep-inventory" as a mod.
return mod_name
end
})
---For Space Exploration mod
local function on_pre_player_died(event)
ensure_data()
local player_index = event.player_index
corpse_pos_data[player_index] = nil
local player = game.get_player(player_index)
if not (player and player.valid) then return end
if player.cheat_mode then return end
corpse_pos_data[player_index] = {player.surface, player.position} -- (it's wrong, but it works)
end
---For Space Exploration mod
local function on_SE_player_respawned(event)
ensure_data()
local player_index = event.player_index
local player = game.get_player(player_index)
if not (player and player.valid) then return end
if player.cheat_mode then
corpse_pos_data[player_index] = nil
return
end
if corpse_pos_data[player_index] == nil then return end
local pos_data = corpse_pos_data[player_index]
local surface = pos_data[1]
if not (surface and surface.valid) then
corpse_pos_data[player_index] = nil
return
end
local position = pos_data[2]
local corpse = surface.find_entity("character-corpse", position)
if not (corpse and corpse.valid) then
corpse_pos_data[player_index] = nil
return
end
local prev_inventory = corpse.get_inventory(defines.inventory.character_corpse)
if not (prev_inventory and prev_inventory.valid) or prev_inventory.is_empty() then
corpse_pos_data[player_index] = nil
return
end
if prev_inventory.valid then
local player_position = player.position
local current_surface = player.surface
local spill_item_stack_param = {
position = player_position, stack = nil,
enable_looted = true, allow_belts = false
}
local armor_inventory = player.get_inventory(defines.inventory.character_armor)
if armor_inventory and armor_inventory.valid then
for i = 1, #prev_inventory do
local stack = prev_inventory[i]
if not stack.valid_for_read then
goto continue
end
if not stack.is_armor then
goto continue
end
if armor_inventory.can_insert(stack) then
armor_inventory.insert(stack)
prev_inventory.remove(stack)
break
end
if player.can_insert(stack) then
player.insert(stack)
else
spill_item_stack_param.stack = stack
current_surface.spill_item_stack(spill_item_stack_param)
end
:: continue ::
end
end
for i = 1, #prev_inventory do
local stack = prev_inventory[i]
if not stack.valid_for_read then
goto continue
end
if player.can_insert(stack) then
player.insert(stack)
else
spill_item_stack_param.stack = stack
current_surface.spill_item_stack(spill_item_stack_param)
end
:: continue ::
end
corpse_inventory[player_index] = nil
end
corpses_queue[player_index] = nil
if corpse and corpse.valid then
corpse.destroy({raise_destroy=true})
end
corpse_pos_data[player_index] = nil
end
local function on_player_died(event)
ensure_data()
local player_index = event.player_index
corpse_inventory[player_index] = nil
local player = game.get_player(player_index)
if not (player and player.valid) then return end
if player.cheat_mode then return end
local surface = player.surface
local position = player.position
-- TOOD: improve
local corpse = surface.find_entity("character-corpse", position)
if not (corpse and corpse.valid) then return end
corpse.operable = false
corpse.minable_flag = false
corpses_queue[player_index] = corpse
local inventory = corpse.get_inventory(defines.inventory.character_corpse)
if not (inventory and inventory.valid) or inventory.is_empty() then return end
corpse_inventory[player_index] = inventory
end
local function on_pre_player_removed(event)
ensure_data()
local player_index = event.player_index
corpses_queue[player_index] = nil
corpse_inventory[player_index] = nil
end
local function on_player_respawned(event)
ensure_data()
local player_index = event.player_index
local player = game.get_player(player_index)
if not (player and player.valid) then return end
if player.cheat_mode then
corpses_queue[player_index] = nil
return
end
local prev_inventory = corpse_inventory[player_index]
if prev_inventory and prev_inventory.valid then
local player_position = player.position
local current_surface = player.surface
local spill_item_stack_param = {
position = player_position, stack = nil,
enable_looted = true, allow_belts = false
}
local armor_inventory = player.get_inventory(defines.inventory.character_armor)
if armor_inventory and armor_inventory.valid then
for i = 1, #prev_inventory do
local stack = prev_inventory[i]
if not stack.valid_for_read then
goto continue
end
if not stack.is_armor then
goto continue
end
if armor_inventory.can_insert(stack) then
armor_inventory.insert(stack)
prev_inventory.remove(stack)
break
end
if player.can_insert(stack) then
player.insert(stack)
else
spill_item_stack_param.stack = stack
current_surface.spill_item_stack(spill_item_stack_param)
end
:: continue ::
end
end
for i = 1, #prev_inventory do
local stack = prev_inventory[i]
if not stack.valid_for_read then
goto continue
end
if player.can_insert(stack) then
player.insert(stack)
else
spill_item_stack_param.stack = stack
current_surface.spill_item_stack(spill_item_stack_param)
end
:: continue ::
end
corpse_inventory[player_index] = nil
end
local corpse = corpses_queue[player_index]
corpses_queue[player_index] = nil
if corpse and corpse.valid then
corpse.destroy({raise_destroy=true})
end
end
local function link_events()
if remote.interfaces["space-exploration"] and remote.interfaces["space-exploration"]["get_on_player_respawned_event"] then
script.on_event(defines.events.on_pre_player_died, on_pre_player_died)
local id = remote.call("space-exploration", "get_on_player_respawned_event")
script.on_event(id, on_SE_player_respawned)
end
end
local function link_data()
mod_data = storage.keep_inventory
if mod_data then
corpses_queue = mod_data.corpses_queue
corpse_inventory = mod_data.corpse_inventory
corpse_pos_data = mod_data.corpse_pos_data
else
corpses_queue = nil
corpse_inventory = nil
corpse_pos_data = nil
end
link_events()
end
local function update_global_data()
local data = ensure_data()
data.players_bodies = {}
link_events()
for player_index, corpse in pairs(corpses_queue) do
if game.get_player(player_index) == nil or corpse.valid == false then
corpses_queue[player_index] = nil
end
end
for player_index, inv in pairs(corpse_inventory) do
if game.get_player(player_index) == nil or inv.valid == false then
corpse_inventory[player_index] = nil
end
end
for player_index in pairs(corpse_pos_data) do
if game.get_player(player_index) == nil then
corpse_pos_data[player_index] = nil
end
end
end
script.on_init(update_global_data)
script.on_configuration_changed(update_global_data)
script.on_load(link_data)
script.on_event(defines.events.on_player_respawned, on_player_respawned)
script.on_event(defines.events.on_player_died, on_player_died)
script.on_event(defines.events.on_pre_player_removed, on_pre_player_removed)
-- This is a part of "gvv", "Lua API global Variable Viewer" mod. https://mods.factorio.com/mod/gvv
-- It makes possible gvv mod to read sandboxed variables in the map or other mod if following code is inserted at the end of empty line of "control.lua" of each.
if script.active_mods["gvv"] then require("gvv.gvv")() end