You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
523 lines
19 KiB
523 lines
19 KiB
local Respawn = {}
|
|
|
|
--custon event
|
|
--/c remote.call("space-exploration", "get_on_player_respawned_event")
|
|
--script.on_event(remote.call("space-exploration", "get_on_player_respawned_event"), my_event_handler)
|
|
Respawn.on_player_respawned_event = script.generate_event_name()
|
|
|
|
-- constants
|
|
Respawn.name_gui = mod_prefix.."respawn-gui"
|
|
Respawn.name_shortcut = mod_prefix.."respawn"
|
|
Respawn.name_button_event = mod_prefix.."respawn"
|
|
Respawn.keep_percent = 0.9
|
|
Respawn.corpse_inventory_size_buffer = 100
|
|
Respawn.non_colliding_search_radius = 128
|
|
|
|
function Respawn.at(player, surface, position)
|
|
position = position or player.force.get_spawn_position(surface) or {0,0}
|
|
player.teleport(position, surface)
|
|
|
|
local character = surface.create_entity{name = "character", position=position, force=player.force, raise_built=true}
|
|
local playerdata = get_make_playerdata(player)
|
|
playerdata.death_surface = nil
|
|
Respawn.set_data(playerdata, character)
|
|
player.set_controller{type = defines.controllers.character, character = character}
|
|
teleport_non_colliding_player(player, position)
|
|
Respawn.close_gui(player)
|
|
RemoteView.stop(player)
|
|
--script.raise_event(defines.events.on_player_respawned, {player_index=player.index}) -- crashes in 0.18.27
|
|
script.raise_event(Respawn.on_player_respawned_event, {player_index = player.index})
|
|
on_player_spawned({player_index=player.index})
|
|
end
|
|
|
|
function Respawn.at_zone(player, zone, position)
|
|
Respawn.at(player, Zone.get_make_surface(zone), position)
|
|
end
|
|
|
|
function Respawn.at_landing_pad(player, landing_pad)
|
|
if not landing_pad.container and landing_pad.container.valid then
|
|
Respawn.open_gui (player)
|
|
return
|
|
end
|
|
local position = landing_pad.container.position or {0,0}
|
|
local surface = landing_pad.container.surface
|
|
Respawn.at(player, surface, position)
|
|
end
|
|
|
|
function Respawn.at_spaceship(player, spaceship)
|
|
local position = {0,0}
|
|
local surface = game.surfaces[1]
|
|
if spaceship.console and spaceship.console.valid then
|
|
position = spaceship.console.position
|
|
surface = spaceship.console.surface
|
|
elseif spaceship.own_surface_index then
|
|
surface = Spaceship.get_own_surface(spaceship)
|
|
if spaceship.known_bounds then
|
|
position = {
|
|
x = (spaceship.known_bounds.left_top.x + spaceship.known_bounds.right_bottom.x) / 2,
|
|
y = (spaceship.known_bounds.left_top.y + spaceship.known_bounds.right_bottom.y) / 2
|
|
}
|
|
end
|
|
end
|
|
Respawn.at(player, surface, position)
|
|
end
|
|
|
|
|
|
function Respawn.get_options(player)
|
|
local home_zone
|
|
if global.forces[player.force.name] and global.forces[player.force.name].homeworld_index then
|
|
home_zone = Zone.from_zone_index(global.forces[player.force.name].homeworld_index)
|
|
end
|
|
if not home_zone then home_zone = Zone.from_name("Nauvis") end
|
|
local options = {
|
|
default = home_zone,
|
|
rocket_landing_pads = {},
|
|
spaceships = {}
|
|
}
|
|
local playerdata = get_make_playerdata(player)
|
|
local death_zone = nil
|
|
if playerdata.death_surface then
|
|
local vault = Ancient.vault_from_surface(playerdata.death_surface)
|
|
if vault then
|
|
death_zone = Zone.from_zone_index(vault.zone_index)
|
|
else
|
|
death_zone = Zone.from_surface(playerdata.death_surface)
|
|
end
|
|
end
|
|
if not death_zone then death_zone = home_zone end
|
|
if global.spaceships then
|
|
local spaceship_zones = {}
|
|
local spaceship_zone = nil
|
|
for _, spaceship in pairs(global.spaceships) do
|
|
if spaceship.force_name == player.force.name then
|
|
spaceship_zone = Zone.from_surface(Spaceship.get_current_surface(spaceship))
|
|
if spaceship_zone then
|
|
table.insert(spaceship_zones, spaceship_zone)
|
|
end
|
|
end
|
|
end
|
|
if #spaceship_zones > 0 then
|
|
local spaceship_respawn_surface = Zone.get_surface(Zone.get_closest_zone(death_zone, spaceship_zones))
|
|
for _, spaceship in pairs(global.spaceships) do
|
|
if spaceship.force_name == player.force.name and Spaceship.get_current_surface(spaceship) == spaceship_respawn_surface then
|
|
table.insert(options.spaceships, spaceship)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
if global.rocket_landing_pads then
|
|
local landing_pad_zones = {}
|
|
for zone_index, zone_assets in pairs(global.forces[player.force.name].zone_assets) do
|
|
if zone_assets.rocket_landing_pad_names and table_size(zone_assets.rocket_landing_pad_names) > 0 then
|
|
table.insert(landing_pad_zones, Zone.from_zone_index(zone_index))
|
|
end
|
|
end
|
|
|
|
if #landing_pad_zones > 0 then
|
|
local respawn_zone = Zone.get_closest_zone(death_zone, landing_pad_zones)
|
|
local landing_pad_respawn_surface = Zone.get_surface(Zone.get_closest_zone(death_zone, landing_pad_zones))
|
|
for _, rocket_landing_pad in pairs(global.rocket_landing_pads) do
|
|
if rocket_landing_pad.force_name == player.force.name then
|
|
if rocket_landing_pad.container and rocket_landing_pad.container.valid then
|
|
if rocket_landing_pad.container.surface == landing_pad_respawn_surface then
|
|
table.insert(options.rocket_landing_pads, rocket_landing_pad)
|
|
end
|
|
else
|
|
Landingpad.destroy(rocket_landing_pad)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return options
|
|
end
|
|
|
|
|
|
|
|
function Respawn.on_gui_click(event)
|
|
if not (event.element and event.element.valid) then return end
|
|
local element = event.element
|
|
local player = game.players[event.player_index]
|
|
root = gui_element_or_parent(element, Respawn.name_gui)
|
|
if not root then return end
|
|
|
|
local options = Respawn.get_options(player)
|
|
if element.name == "spaceship" and #options.spaceships > 0 then
|
|
Respawn.at_spaceship(player, options.spaceships[math.random(#options.spaceships)])
|
|
elseif element.name == "rocket-landing-pad" and #options.rocket_landing_pads > 0 then
|
|
Respawn.at_landing_pad(player, options.rocket_landing_pads[math.random(#options.rocket_landing_pads)])
|
|
else
|
|
Respawn.at_zone(player, options.default)
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_gui_click, Respawn.on_gui_click)
|
|
|
|
function Respawn.close_gui (player)
|
|
if player.character and player.gui.center[Respawn.name_gui] then
|
|
player.gui.center[Respawn.name_gui].destroy()
|
|
end
|
|
end
|
|
|
|
function Respawn.open_gui (player)
|
|
local gui = player.gui.center
|
|
gui.clear()
|
|
local options = Respawn.get_options(player)
|
|
local root = gui.add{ type = "frame", name = Respawn.name_gui, direction="vertical", caption={"space-exploration.respawn-options-title"}}
|
|
flow = root.add{ type="flow", name="respawn_options", direction="vertical"}
|
|
--flow.add{ type = "sprite-button", sprite = "virtual-signal/"..mod_prefix.."planet", name = "planet"}
|
|
flow.add{ type = "button", caption = {"space-exploration.respawn-button-homeworld"}, name = "planet"}
|
|
if #options.rocket_landing_pads > 0 then
|
|
--flow.add{ type = "sprite-button", sprite = "item/"..mod_prefix.."rocket-landing-pad", name = "rocket-landing-pad"}
|
|
flow.add{ type = "button", caption = {"space-exploration.respawn-button-landing-pad"}, name = "rocket-landing-pad"}
|
|
end
|
|
if #options.spaceships > 0 then
|
|
--flow.add{ type = "sprite-button", sprite = "virtual-signal/"..mod_prefix.."spaceship", name = "spaceship"}
|
|
flow.add{ type = "button", caption = {"space-exploration.respawn-button-spaceship"}, name = "spaceship"}
|
|
end
|
|
end
|
|
|
|
function Respawn.set_data(playerdata, character)
|
|
|
|
if playerdata.personal_logistic_slots then
|
|
for i, slot in pairs(playerdata.personal_logistic_slots) do
|
|
if slot and slot.name and game.item_prototypes[slot.name] then
|
|
if slot.min then
|
|
if slot.max then
|
|
slot.min = math.min(slot.min, slot.max)
|
|
end
|
|
slot.min = math.max(0, slot.min)
|
|
end
|
|
if slot.max then
|
|
if slot.min then
|
|
slot.max = math.max(slot.min, slot.max)
|
|
end
|
|
slot.max = math.max(0, slot.max)
|
|
end
|
|
character.set_personal_logistic_slot(i, slot)
|
|
end
|
|
end
|
|
end
|
|
|
|
if playerdata.character_personal_logistic_requests_enabled ~= nil then
|
|
character.character_personal_logistic_requests_enabled = playerdata.character_personal_logistic_requests_enabled
|
|
end
|
|
|
|
if playerdata.inventory_type_filters then
|
|
for inventory_type, inventory_filters in pairs(playerdata.inventory_type_filters) do
|
|
local inventory = character.get_inventory(inventory_type)
|
|
if inventory then
|
|
for slot, string in pairs(inventory_filters) do
|
|
if string and slot <= #inventory and game.item_prototypes[string] then
|
|
inventory.set_filter(slot, string)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
function Respawn.record_data(playerdata, character)
|
|
playerdata.inventory_type_filters = {}
|
|
playerdata.personal_logistic_slots = {}
|
|
playerdata.character_personal_logistic_requests_enabled = nil
|
|
|
|
if not character and character.valid then return end
|
|
|
|
local limit = 100
|
|
local i = 1
|
|
while i < limit do
|
|
playerdata.personal_logistic_slots[i] = character.get_personal_logistic_slot(i)
|
|
if playerdata.personal_logistic_slots[i] and playerdata.personal_logistic_slots[i].name then
|
|
limit = i + 100
|
|
end
|
|
i = i + 1
|
|
end
|
|
|
|
playerdata.character_personal_logistic_requests_enabled= character.character_personal_logistic_requests_enabled
|
|
|
|
local inventories = {defines.inventory.character_main, defines.inventory.character_guns, defines.inventory.character_ammo}
|
|
for _, inventory_type in pairs(inventories) do
|
|
local inventory = character.get_inventory(inventory_type)
|
|
if inventory then
|
|
playerdata.inventory_type_filters[inventory_type] = playerdata.inventory_type_filters[inventory_type] or {}
|
|
for i = 1, #inventory do
|
|
playerdata.inventory_type_filters[inventory_type][i] = inventory.get_filter(i)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function Respawn.tick_task_bind_corpse (tick_task)
|
|
tick_task.valid = false
|
|
local corpse = tick_task.corpse_surface.find_entity("character-corpse", tick_task.corpse_position)
|
|
if corpse and corpse.valid then
|
|
corpse.character_corpse_player_index = tick_task.player_index
|
|
end
|
|
end
|
|
|
|
function Respawn.mark_corpse (player, character)
|
|
player.force.add_chart_tag(character.surface, {
|
|
icon = {type = "virtual", name = mod_prefix .. "character-corpse"},
|
|
position = character.position,
|
|
text = player.name
|
|
})
|
|
local tick_task = new_tick_task("bind-corpse")
|
|
tick_task.corpse_position = character.position
|
|
tick_task.corpse_surface = character.surface
|
|
tick_task.player_index = player.index
|
|
|
|
end
|
|
|
|
function Respawn.respawn (player)
|
|
|
|
close_own_guis(player)
|
|
player.play_sound{path = "se-game-lost", volume = 1}
|
|
|
|
local playerdata = get_make_playerdata(player)
|
|
if player.character then
|
|
Respawn.mark_corpse(player, player.character)
|
|
Respawn.record_data(playerdata, player.character)
|
|
end
|
|
playerdata.character = nil
|
|
player.set_controller{type = defines.controllers.spectator}
|
|
|
|
if playerdata.lock_respawn then
|
|
if playerdata.lock_respawn.surface_name and game.surfaces[playerdata.lock_respawn.surface_name] then
|
|
Respawn.at(player, game.surfaces[playerdata.lock_respawn.surface_name], playerdata.lock_respawn.position)
|
|
end
|
|
if playerdata.lock_respawn.zone_index then
|
|
local zone = Zone.from_zone_index(playerdata.lock_respawn.zone_index)
|
|
Respawn.at(player, Zone.get_make_surface(zone), playerdata.lock_respawn.position)
|
|
end
|
|
return
|
|
end
|
|
|
|
local options = Respawn.get_options(player)
|
|
--if #options.rocket_landing_pads > 0 or #options.spaceships > 0 then
|
|
Respawn.open_gui (player)
|
|
--else
|
|
-- Respawn.at_zone(player, options.default)
|
|
--end
|
|
end
|
|
|
|
function Respawn.die (player)
|
|
|
|
RemoteView.stop(player)
|
|
Spaceship.stop_anchor_scouting(player)
|
|
|
|
if player.character then
|
|
player.character.die()
|
|
else -- skip ahead
|
|
local playerdata = get_make_playerdata(player)
|
|
if playerdata.character and playerdata.character.valid then
|
|
Respawn.record_data(playerdata, character)
|
|
playerdata.character.die()
|
|
game.print({"space-exploration.player-died", player.name or ("Player " .. player.index)})
|
|
end
|
|
Respawn.respawn(player)
|
|
end
|
|
|
|
if player and player.connected then
|
|
player.print({"space-exploration.please-consider-patreon"})
|
|
end
|
|
end
|
|
|
|
function Respawn.on_pre_player_died (event)
|
|
local player = game.players[event.player_index]
|
|
if player then
|
|
RemoteView.stop(player)
|
|
Spaceship.stop_anchor_scouting(player)
|
|
local playerdata = get_make_playerdata(player)
|
|
playerdata.death_surface = player.character.surface
|
|
Respawn.respawn(player)
|
|
if player and player.connected then
|
|
player.print({"space-exploration.please-consider-patreon"})
|
|
end
|
|
--[[
|
|
local surface = player.character.surface
|
|
player.character.health = player.character.prototype.max_health
|
|
local main_inv = player.get_main_inventory()
|
|
local corpse = surface.create_entity{
|
|
name = player.character.prototype.character_corpse.name,
|
|
position = player.position,
|
|
force = player.force,
|
|
player_index = player.index,
|
|
inventory_size = #main_inv + Respawn.corpse_inventory_size_buffer
|
|
}
|
|
--corpse.expires = false -- errors
|
|
corpse.character_corpse_player_index = player.index
|
|
corpse.character_corpse_tick_of_death = game.tick
|
|
for _, inv_type in pairs({
|
|
defines.inventory.character_main,
|
|
defines.inventory.character_guns,
|
|
defines.inventory.character_ammo,
|
|
defines.inventory.character_armor,
|
|
defines.inventory.character_trash,
|
|
}) do
|
|
local inv = player.get_inventory(inv_type)
|
|
if inv then
|
|
local contents = inv.get_contents()
|
|
for item_type, item_count in pairs(contents) do
|
|
if inv_type == defines.inventory.character_armor then -- never lose this
|
|
corpse.insert({name=item_type, count=item_count})
|
|
elseif item_count > 1 then
|
|
corpse.insert({name=item_type, count=math.ceil(item_count * Respawn.keep_percent) })
|
|
elseif math.random() < Respawn.keep_percent then
|
|
corpse.insert({name=item_type, count=item_count})
|
|
end
|
|
end
|
|
inv.clear()
|
|
end
|
|
end
|
|
game.print({"space-exploration.player-died", player.name or ("Player " .. player.index)})
|
|
local nauvis = game.surfaces[1]
|
|
player.teleport(nauvis.find_non_colliding_position(player.character.name, {x=0,y=0}, Respawn.non_colliding_search_radius, 1), nauvis)
|
|
]]--
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_pre_player_died, Respawn.on_pre_player_died)
|
|
|
|
|
|
function Respawn.on_entity_died (event)
|
|
-- Called when an entity dies.
|
|
-- entity :: LuaEntity
|
|
-- cause :: LuaEntity (optional): The entity that did the killing if available.
|
|
-- loot :: LuaInventory: The loot generated by this entity if any.
|
|
-- force :: LuaForce (optional): The force that did the killing if any.
|
|
if not (event.entity and event.entity.valid) then return end
|
|
if event.entity.type == "character" then
|
|
local character = event.entity
|
|
if not character.player then
|
|
for player_index, playerdata in pairs(global.playerdata) do
|
|
if playerdata.character == character then
|
|
local player = game.players[player_index]
|
|
if player and player.connected then
|
|
-- player's character died
|
|
RemoteView.stop(player)
|
|
Spaceship.stop_anchor_scouting(player)
|
|
|
|
Respawn.record_data(playerdata, character)
|
|
game.print({"space-exploration.player-died", player.name or ("Player " .. player.index)})
|
|
Respawn.respawn(player) -- adds marker
|
|
end
|
|
return
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_entity_died, Respawn.on_entity_died)
|
|
|
|
function Respawn.on_corpse_removed (corpse)
|
|
|
|
for _, force in pairs(game.forces) do
|
|
local tags = force.find_chart_tags(corpse.surface, Util.position_to_area( corpse.position, 1))
|
|
for _, tag in pairs(tags) do
|
|
if tag.icon and tag.icon.name == mod_prefix .. "character-corpse" then
|
|
-- TODO: check that corpse matches the marker.
|
|
-- will need to have the corpse know the player first.
|
|
tag.destroy()
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
function Respawn.on_character_corpse_expired (event)
|
|
-- event.corpse LuaEntity
|
|
-- Note: this is not called if the corpse is mined. See defines.events.on_pre_player_mined_item to detect that.
|
|
-- remove corpse marker
|
|
if not(event.corpse and event.corpse.valid) then return end
|
|
Respawn.on_corpse_removed (event.corpse)
|
|
|
|
end
|
|
Event.addListener(defines.events.on_character_corpse_expired, Respawn.on_character_corpse_expired)
|
|
|
|
function Respawn.on_pre_player_mined_item (event)
|
|
-- event.entity
|
|
-- event.player_index
|
|
-- remove corpse marker
|
|
if not(event.entity and event.entity.valid) then return end
|
|
if event.entity.name == "character-corpse" then
|
|
Respawn.on_corpse_removed (event.entity)
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_pre_player_mined_item, Respawn.on_pre_player_mined_item)
|
|
|
|
function Respawn.on_lua_shortcut (event)
|
|
if event.player_index
|
|
and game.players[event.player_index]
|
|
and game.players[event.player_index].connected then
|
|
if event.prototype_name == Respawn.name_shortcut then
|
|
--Respawn.die(game.players[event.player_index])
|
|
Respawn.confirm.open_gui(game.players[event.player_index])
|
|
end
|
|
end
|
|
end
|
|
Event.addListener(defines.events.on_lua_shortcut, Respawn.on_lua_shortcut)
|
|
|
|
function Respawn.on_respawn_keypress (event)
|
|
if event.player_index
|
|
and game.players[event.player_index]
|
|
and game.players[event.player_index].connected
|
|
then
|
|
--Respawn.die(game.players[event.player_index])
|
|
Respawn.confirm.open_gui(game.players[event.player_index])
|
|
end
|
|
end
|
|
Event.addListener(Respawn.name_button_event, Respawn.on_respawn_keypress)
|
|
|
|
Respawn.confirm = {}
|
|
Respawn.confirm.name_gui = mod_prefix.."respawn-gui-confirm"
|
|
Respawn.confirm.buttons_container = 'confirm_button_container'
|
|
Respawn.confirm.button_yes = 'yes'
|
|
Respawn.confirm.button_no = 'no'
|
|
|
|
function Respawn.confirm.open_gui(player)
|
|
local center = player.gui.center
|
|
if (center[Respawn.confirm.name_gui]) then return end
|
|
if (center[Respawn.name_gui]) then return end
|
|
local popup = center.add {type = "frame", name = Respawn.confirm.name_gui, direction = "vertical", caption = {'space-exploration.respawn-confirm-title'}}
|
|
|
|
local buttons_container = popup.add {type = "flow", name = Respawn.confirm.buttons_container, direction = "horizontal"}
|
|
|
|
|
|
buttons_container.add {
|
|
type = "button",
|
|
name = Respawn.confirm.button_no,
|
|
style="back_button",
|
|
caption = {'space-exploration.respawn-confirm-no'}
|
|
}
|
|
|
|
local yes = buttons_container.add {
|
|
type = "button",
|
|
name = Respawn.confirm.button_yes,
|
|
style="red_confirm_button",
|
|
caption = {'space-exploration.respawn-confirm-yes'}
|
|
}
|
|
yes.style.left_margin = 10
|
|
end
|
|
|
|
function Respawn.confirm.close_gui(player)
|
|
if player.gui.center[Respawn.confirm.name_gui] then
|
|
player.gui.center[Respawn.confirm.name_gui].destroy()
|
|
end
|
|
end
|
|
|
|
function Respawn.confirm.on_gui_click(event)
|
|
if not (event.element and event.element.valid) then return end
|
|
local element = event.element
|
|
local player = game.players[event.player_index]
|
|
root = gui_element_or_parent(element, Respawn.confirm.name_gui)
|
|
if not root then return end
|
|
if element.name == Respawn.confirm.button_yes then
|
|
Respawn.die(game.players[event.player_index])
|
|
end
|
|
Respawn.confirm.close_gui(game.players[event.player_index])
|
|
end
|
|
Event.addListener(defines.events.on_gui_click, Respawn.confirm.on_gui_click)
|
|
|
|
|
|
return Respawn
|
|
|