Built for the Nova Zombie Community zombie hunting server (Nova HunterZ CS Clan).
It is a standalone plugin though. There is nothing zombie specific in it and no dependency or anything else — it will run on any CS2 server that uses bots. Deathmatch, casual, retakes, surf, a practice server, whatever you have.
Makes bots get a visible tag over their head. Every bot on the opposite team gets a small floating label above its head. Your own side's bots get nothing extra, because the game has already handled them.
The label turns to face you, drops when the bot crouches, follows through respawns and model swaps, and cleans itself up properly when the round ends or the map changes.
Unlike the teammate markers, an enemy tag does not draw through walls. It sits in the world and gets occluded like anything else, so it tells you where a bot is once you can already see it — not where one is hiding.
- Coloured by the bot's team, in the game's own blue and amber
- Drawn in Stratum2, the game's UI font, unlit so it reads the same in any light
- Optional downward arrow under the text
- Never tags real players, HLTV, spectators, or a bot somebody has taken over
- Survives infections, respawns, team switches, kicks and hot reloads
BOT
▼
(o_o)
- A CS2 server running SwiftlyS21.4.5 or newer
- The SwiftlyS2 managed (.NET) runtime enabled
Grab
BotTagsManager.zipfrom the Releases page.Extract it into your server's
addons/swiftlys2/plugins/folder. You should end up with:addons/swiftlys2/plugins/BotTagsManager/BotTagsManager.dllRestart the server, or load it live:
sw plugins load BotTagsManagerA commented config is written for you on first load at:
addons/swiftlys2/configs/plugins/BotTagsManager/BotTagsManager.jsonc
That is it. Bots get tagged from the next spawn.
You need the .NET 10 SDK.
git clone https://github.com/<you>/BotTagsManager.git
cd BotTagsManager
dotnet publish -c ReleaseThe DLL lands in build/publish/BotTagsManager/, and a ready-to-drop
build/BotTagsManager.zip is written next to it.
| Command | Chat | Permission | What it does |
|---|---|---|---|
sw_botags status | !botags status | bottags/admin | Shows the current settings and how many tags are live |
sw_botags reload | !botags reload | bottags/admin | Re-reads the config and rebuilds every tag |
reload rebuilds rather than patches, on purpose. Font, size and scale are all baked in
when the entity spawns, so patching would leave half the settings stale and make it look
like the reload had not worked. Handy for tuning colour and height without bouncing the
server between tests.
Grant the permission in addons/swiftlys2/configs/core/permissions.jsonc:
Set AdminPermission to "" in the config if you would rather anyone could run it.
Everything lives in BotTagsManager.jsonc. The file written on first load has all of this
in it as comments.
| Setting | Default | What it does |
|---|---|---|
Enabled | true | Off loads the plugin but tags nothing |
TagText | "BOT" | The word above the arrow. Short is better — past about six characters it looks silly |
ShowArrow | true | The little triangle under the text |
DefaultColor | "255 210 0" | "R G B", 0-255. Only used when team colours are off |
TeamSpecificColors | true | Colour by the bot's team instead of using DefaultColor |
CounterTerroristColor | "176 218 231" | Only used when team colours are on |
TerroristColor | "251 225 105" | Only used when team colours are on |
Font | "Stratum2" | Must exist on the client. Arial, Arial Black and Verdana are safe fallbacks |
Opacity | 255 | How solid the tag is, 0-255 |
FontSize | 80.0 | Rasterised size, scaled down by WorldUnitsPerPx |
WorldUnitsPerPx | 0.09 | World height of a line is roughly FontSize × this |
BaseHeight | 86.0 | Height above the bot |
DuckHeightDelta | 18.0 | How far the tag drops when fully crouched |
TrackDuck | true | Off leaves the tag at BaseHeight and ignores the crouch |
ReconcileIntervalSeconds | 0.25 | How often everything is swept and repaired |
CrouchIntervalSeconds | 0.05 | How often the crouch is checked |
IgnoreWarmupBots | false | Skip tagging during warmup |
AdminPermission | "bottags/admin" | Permission for sw_botags. Blank lets anyone run it |
DefaultColor is amber.
TeamSpecificColors colours by the bot's own team, in the same blue and amber the game
uses over your own side. Turn it off and every bot gets DefaultColor instead, which reads
better on a server where bots change sides mid-round.
Font defaults to Stratum2, the game's own UI face — it ships with CS2 as
csgo/panorama/fonts/stratum2.uifont and the client registers it, so it is the same
lettering as the stock markers. If a client cannot resolve it the arrow renders as a box;
Arial Black is the closest safe fallback.
FontSize and WorldUnitsPerPx work together. The text is rasterised into a texture at
FontSize and then scaled into the world by WorldUnitsPerPx. Turning FontSize down to
make the tag smaller gives you a low-resolution texture that looks mushy up close — leave it
high and lower WorldUnitsPerPx instead.
BaseHeight and DuckHeightDelta — the pawn origin sits at the feet, the standing hull
is 72 units and the crouched hull is 54. So 86 clears the head with a bit of room, and a
delta of 18 keeps that gap identical in both stances.
CrouchIntervalSeconds is how often a bot's crouch is checked. A tag rides its bot on
the client, so this only sends anything when the bot actually ducks or stands back up.
IgnoreWarmupBots reads the game's own m_bWarmupPeriod. If your server's warmup is a
mod's own implementation rather than the native one, this will not see it.
The tag rides the bot itself rather than being moved into place by the server. That is what makes it glide instead of stepping from one spot to the next as the bot runs.
It is deliberately not attached to a head bone, though. A bone follows the animation, so the tag would bob and sway with every run cycle and idle twitch. Driving the height off the crouch amount instead keeps it rock steady while still following the duck.
Depth offset is left at zero on purpose. That is what keeps the tag behind walls instead of in front of them.
The tag is drawn fullbright, so a bot in a dark corner reads exactly the same as one stood in the sun.
Two things about the stock markers this cannot copy, both because a tag is one world entity shared by everybody rather than a HUD element drawn per player:
Everything is destroyed at round end. That is not just tidiness, the round restart runs the engine's entity preserve pass, and a plugin-spawned entity still alive when it happens is a known way to crash clients. The sweep puts every tag back within a quarter second of the respawn anyway.
MIT. See LICENSE.
{ "Permissions": { "Players": { "76561198000000000": [ "bottags/admin" ] } } }