local player_system = {} local function hide_dot(player) if player and player.valid then player.show_on_map = false end end local function maintain_light(player) if not (player and player.valid) then return end storage.player_lights = storage.player_lights or {} local light = storage.player_lights[player.index] if not player.character or not player.character.valid then if light and light.valid then light.destroy() end storage.player_lights[player.index] = nil return end if not light or not light.valid then storage.player_lights[player.index] = rendering.draw_light{sprite = "utility/light_medium", target = player.character, surface = player.character.surface, players = {player}, scale = 1.2, intensity = .8, minimum_darkness = .2} end end -- show_on_map is all-or-nothing, so allies are revealed with rendering objects -- whose players list is restricted to the owner's force instead. -- Name over the character in-world and on the map, plus a dot on the map. -- Each is separate because render_mode is per-object. local ALLY_MARKER_SPECS = { {key = "name_game", mode = "game", text = true}, {key = "name_chart", mode = "chart", text = true}, {key = "dot_chart", mode = "chart", radius = 2}, } local function is_team_force(force_name) for _, name in pairs(storage.planet_teams or {}) do if name == force_name then return true end end return false end local function maintain_marker(player) if not (player and player.valid) then return end storage.ally_markers = storage.ally_markers or {} local markers = storage.ally_markers[player.index] local viewers = {} if player.character and player.character.valid and player.force and is_team_force(player.force.name) then for _, ally in pairs(player.force.players) do viewers[#viewers + 1] = ally.index end end if #viewers == 0 then if markers then for _, marker in pairs(markers) do if marker.valid then marker.destroy() end end end storage.ally_markers[player.index] = nil return end local color = (storage.force_colors and storage.force_colors[player.force.name]) or {r = 1, g = 1, b = 1, a = 1} markers = markers or {} for _, spec in pairs(ALLY_MARKER_SPECS) do local marker = markers[spec.key] if marker and marker.valid and marker.surface ~= player.character.surface then marker.destroy() marker = nil markers[spec.key] = nil end if not (marker and marker.valid) then if spec.text then marker = rendering.draw_text{ text = player.name, surface = player.character.surface, target = player.character, target_offset = {0, -2.5}, color = color, scale = 1.5, render_mode = spec.mode, players = viewers, } else marker = rendering.draw_circle{ surface = player.character.surface, target = player.character, color = color, radius = spec.radius, filled = true, render_mode = spec.mode, players = viewers, } end markers[spec.key] = marker else marker.players = viewers marker.target = player.character marker.color = color end end storage.ally_markers[player.index] = markers end -- Vanilla pins can bind to a player or their character, leaking a live -- tracker arrow that defeats show_on_map = false. Drop any pin that targets -- a player/character of another force. local function clear_enemy_pins(player) for _, pin in pairs(player.get_pins()) do local pinned = pin.player local leak = pinned and pinned.valid and pinned.force ~= player.force if not leak then for _, target in pairs(pin.targets or {}) do if target.valid and target.type == "character" and target.force ~= player.force then leak = true break end end end if leak and pin.valid then pin.destroy() end end end function player_system.on_init() storage.player_lights = storage.player_lights or {} end function player_system.on_player_created(event) local player = game.get_player(event.player_index) hide_dot(player); maintain_light(player) end function player_system.on_player_joined_game(event) hide_dot(game.get_player(event.player_index)) end function player_system.on_player_respawned(event) hide_dot(game.get_player(event.player_index)); maintain_light(game.get_player(event.player_index)) end function player_system.on_player_changed_surface(event) local player = game.get_player(event.player_index) if player and storage.player_lights[player.index] then local light = storage.player_lights[player.index] if light and light.valid then light.destroy() end storage.player_lights[player.index] = nil end maintain_light(player) end function player_system.on_light_tick() for _, player in pairs(game.connected_players) do maintain_light(player) maintain_marker(player) clear_enemy_pins(player) if player.character and player.character.valid then player.character.disable_flashlight() end end end return player_system