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