diff --git a/changelog.md b/changelog.md index 023256a..086dcdd 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project does not adhere to Semantic Versioning. ## [Unreleased] +### Added +* Added `teams.hiddenPlayers` (theme `raw`) to hide autokilled coaches in Faceit matches ## [2.3.1] - 2024-01-21 diff --git a/docs/hiding-players.md b/docs/hiding-players.md new file mode 100644 index 0000000..59ed212 --- /dev/null +++ b/docs/hiding-players.md @@ -0,0 +1,12 @@ +# Hiding Players + +You can hide specific players if you don't want them to show up at all in the HUD. +This may be useful for coaches that are misrepresented as dead players. +Please note that if you hide a real player, you will run into issues. + +To hide a player, use the `teams.hiddenPlayers` option on the HUD config page at http://127.0.0.1:31982/config. +Provide either a player's SteamID64, or a player's name. +Provide one player per line. + +If you use a player's name, you'll need to exactly match the name that would otherwise be displayed by the HUD. +If you've changed their name using [Player Name Overrides](https://github.com/drweissbrot/cs-hud/blob/master/docs/player-name-overrides.md), use the name that you used for the override. diff --git a/src/themes/raw/gsi/helpers/hidden-players.js b/src/themes/raw/gsi/helpers/hidden-players.js new file mode 100644 index 0000000..1c4dba3 --- /dev/null +++ b/src/themes/raw/gsi/helpers/hidden-players.js @@ -0,0 +1,24 @@ +import { options } from '/hud/core/state.js' + +export const getHiddenPlayers = () => { + const hiddenPlayerNames = new Set() + const hiddenPlayerSteam64Ids = new Set() + + const opt = options['teams.hiddenPlayers']?.trim() + if (! opt?.length) return { hiddenPlayerNames, hiddenPlayerSteam64Ids } + + const lines = opt.split('\n') + + for (const rawLine of lines) { + const line = rawLine.trim() + if (! line?.length) continue + + if (/^7656\d+$/.test(line)) { + hiddenPlayerSteam64Ids.add(line) + } else { + hiddenPlayerNames.add(line) + } + } + + return { hiddenPlayerNames, hiddenPlayerSteam64Ids } +} diff --git a/src/themes/raw/gsi/parse-players.js b/src/themes/raw/gsi/parse-players.js index 5c4e89b..ed12d5e 100644 --- a/src/themes/raw/gsi/parse-players.js +++ b/src/themes/raw/gsi/parse-players.js @@ -2,6 +2,7 @@ import { additionalState, gsiState, options } from '/hud/core/state.js' import { parsePosition } from '/hud/gsi/parse-position.js' import { grenadeOrderIndices } from '/hud/gsi/helpers/grenade-order-indices.js' import { getPlayerNameOverrides } from '/hud/gsi/helpers/player-name-overrides.js' +import { getHiddenPlayers } from '/hud/gsi/helpers/hidden-players.js' // a CS2 update mid-November 2023 changed observer slots to be `0` for the player with the hotkey `1`, `1` for hotkey `2`, ..., `9` for hotkey `0` const getObserverSlot = (player, steam64Id) => { @@ -12,50 +13,60 @@ const getObserverSlot = (player, steam64Id) => { return rawSlot + 1 } +const parsePlayerWeapons = (player) => Object.values(player.weapons).flatMap((weapon) => { + const parsed = { + ammoClip: weapon.ammo_clip, + ammoClipMax: weapon.ammo_clip_max, + ammoReserve: weapon.ammo_reserve, + isActive: weapon.state === 'active', + isBomb: weapon.type === 'C4', + isGrenade: weapon.type === 'Grenade', + isMelee: weapon.type === 'Knife', + isPistol: weapon.type === 'Pistol', + isPrimary: ['Machine Gun', 'Rifle', 'Shotgun', 'SniperRifle', 'Submachine Gun'].includes(weapon.type), + isTaser: weapon.name === 'weapon_taser', // weapon.type is undefined for taser + name: weapon.name, + paintkit: weapon.paintkit, + state: weapon.state, + type: weapon.type, + unprefixedName: weapon.name.replace(/^weapon_/, ''), + } + + if (parsed.name === 'weapon_flashbang' && parsed.ammoReserve >= 2) return [parsed, { ...parsed, isActive: false }] + + return [parsed] +}).sort((a, b) => { + if (a.isGrenade && b.isGrenade) { + return grenadeOrderIndices[a.name] - grenadeOrderIndices[b.name] + } + + if (a.isGrenade) return 1 + if (b.isGrenade) return -1 + + if (a.name > b.name) return 1 + if (a.name < b.name) return -1 + + if (a.isActive !== b.isActive) return b.isActive - a.isActive + + return 0 +}) + // TODO if we want an `isBot` or similar: bots appear to use steam ids, starting at 76561197960265729 and counting up from there (these are real steam64Ids though, belonging to real Steam users) export const parsePlayers = () => { const playerNameOverrides = getPlayerNameOverrides() + const { hiddenPlayerNames, hiddenPlayerSteam64Ids } = getHiddenPlayers() - return Object.entries(gsiState.allplayers).map(([steam64Id, player]) => { - const weapons = Object.values(player.weapons).flatMap((weapon) => { - const parsed = { - ammoClip: weapon.ammo_clip, - ammoClipMax: weapon.ammo_clip_max, - ammoReserve: weapon.ammo_reserve, - isActive: weapon.state === 'active', - isBomb: weapon.type === 'C4', - isGrenade: weapon.type === 'Grenade', - isMelee: weapon.type === 'Knife', - isPistol: weapon.type === 'Pistol', - isPrimary: ['Machine Gun', 'Rifle', 'Shotgun', 'SniperRifle', 'Submachine Gun'].includes(weapon.type), - isTaser: weapon.name === 'weapon_taser', // weapon.type is undefined for taser - name: weapon.name, - paintkit: weapon.paintkit, - state: weapon.state, - type: weapon.type, - unprefixedName: weapon.name.replace(/^weapon_/, ''), - } - - if (parsed.name === 'weapon_flashbang' && parsed.ammoReserve >= 2) return [parsed, { ...parsed, isActive: false }] - - return [parsed] - }).sort((a, b) => { - if (a.isGrenade && b.isGrenade) { - return grenadeOrderIndices[a.name] - grenadeOrderIndices[b.name] - } - - if (a.isGrenade) return 1 - if (b.isGrenade) return -1 - - if (a.name > b.name) return 1 - if (a.name < b.name) return -1 - - if (a.isActive !== b.isActive) return b.isActive - a.isActive - - return 0 - }) + const players = [] + + for (const [steam64Id, player] of Object.entries(gsiState.allplayers)) { + if (hiddenPlayerSteam64Ids.has(steam64Id)) continue + const name = playerNameOverrides.get(steam64Id) || player.name + if (hiddenPlayerNames.has(name)) continue + + const weapons = parsePlayerWeapons(player) const grenades = [] + let bomb let knife let primary @@ -73,11 +84,12 @@ export const parsePlayers = () => { const kdRatio = player.match_stats?.kills / (player.match_stats?.deaths || 1) - return { + players.push({ bomb, grenades, kdRatio, knife, + name, primary, secondary, steam64Id, @@ -110,7 +122,6 @@ export const parsePlayers = () => { kills: player.match_stats?.kills, money: player.state?.money, mvps: player.match_stats?.mvps, - name: playerNameOverrides.get(steam64Id) || player.name, observerSlot: getObserverSlot(player, steam64Id), position: parsePosition(player.position), roundDamage: player.state?.round_totaldmg, @@ -121,8 +132,10 @@ export const parsePlayers = () => { score: player.match_stats?.score, side: player.team === 'CT' ? 3 : 2, team: undefined, - } - }).sort((a, b) => { + }) + } + + return players.sort((a, b) => { const x = a.observerSlot === 0 ? 10 : a.observerSlot const y = b.observerSlot === 0 ? 10 : b.observerSlot return x - y diff --git a/src/themes/raw/theme.json b/src/themes/raw/theme.json index e2c9ac6..bfdf633 100644 --- a/src/themes/raw/theme.json +++ b/src/themes/raw/theme.json @@ -44,6 +44,12 @@ "type": "text", "section": "Teams", "label": "List of SteamID64s and their player name for overrides; see https://github.com/drweissbrot/cs-hud/blob/master/docs/player-name-overrides.md" + }, + + "teams.hiddenPlayers": { + "type": "text", + "section": "Teams", + "label": "List of names or SteamID64s of players that should not be shown in the HUD (e.g. coaches); see https://github.com/drweissbrot/cs-hud/blob/master/docs/hiding-players.md" } } }