Custom GOAP-Driven Bot AI for Counter-Strike 2
This is a CounterStrikeSharp plugin I put together that completely overhauls how bots handle close-quarters combat. If you've messed with CS2 bots, you know they aren't great at aggressively rushing with melee weapons or tasers.
To fix this, I implemented a custom Goal-Oriented Action Planning (GOAP) architecture. Because fighting the native CS2 bot pathing is a nightmare, this script uses a hybrid approach: it lets the native CS2 navmesh handle basic map traversal, and only hijacks the bot's motor controls the second they get into a fight.
Here’s a breakdown of how the brain actually works under the hood.
I built a lightweight GOAP setup specifically designed not to tank server performance on 64-tick.
GoapAction: Things the bot can actually do (equip a knife, sprint, dodge).GoapGoal: What the bot wants to achieve right now (e.g., survive, kill). Priority shifts dynamically every frame.
Bots don't just react instantly; they have a short-term memory system.
WorkingMemory: Bots track yourLastKnownPosition. If you break line-of-sight, the bot's "Confidence" in that memory decays over time. Hide long enough, and they literally forget you exist.BotBlackboard: This is the bot's scratchpad for the current tick. It stores where they want to move, how fast, where to aim, and what buttons they need to press (+jump, +duck).
To stop the bots from blatantly tracking you through walls across the map, the sensory system has strict physical checks. A target has to be close (< 750f), on a similar floor (zDiff < 350f), in the bot's FOV, and natively "spotted" by the CS2 engine.
Once a player is spotted, the ThreatLevel spikes, and my custom GOAP engine forcefully takes the wheel. If you hide, the threat drops, and the native CS2 navmesh takes over again so the bot doesn't just stand there staring at a wall.
This is where the bots get actually dangerous. Instead of just W-keying at you, they have some logic to make them harder to hit:
- Weapon Swapping: If you're far away, they pull out the knife to maximize sprint speed. Once they get within 450 units, they hot-swap to the Zeus.
- Fanning Out: I used some basic trig (
Math.Cos+ the bot's entity index) so that if 5 bots rush you, they fan out in a circle instead of forming a single-file line. - Evasive Movement: While rushing, they use randomized state machines to B-hop, crouch-slide, and ADAD strafe.
Standard GOAP systems use heavy A* graph-search algorithms to build a plan. That's way too heavy to run every tick for multiple bots on a CS2 server. Instead, the planner here is a highly optimized, hardcoded decision tree. It's cheap on the CPU but still spits out a solid queue of actions based on distance and what weapon the bot is holding.
Working with the Source 2 API to override bot pathing is currently pretty restrictive. To actually make the bots do what the Blackboard wants, I had to use a few workarounds:
- Organic Aiming: Instead of locking right to a head bone, the aimbot injects sine/cosine noise into the target vectors. The crosshair naturally drifts around the upper chest/shoulders, and turn speed is clamped so it looks like smooth mouse tracking rather than an instant snap.
- Motor Injection: Every server tick, the script uses
pawn.Teleport()to forcefully shove the bot's velocity in the direction it wants to go. - Button Forcing: To make the bots actually swing the knife or shoot the taser, the script manually flips bits inside the
MovementServices.Buttons.ButtonStatesarray. This tricks the server into thinking the bot actually pressed the jump, duck, or attack keys.
The plugin includes a chat command system to manage the bots in real-time. Commands can be typed in chat with or without the ! prefix (e.g., !zeusbots or zeusbots).
These commands control the bot AI and population.
!zeusbots: Enables the custom Zeus/Knife GOAP AI.!normalbots: Disables the custom AI, reverting bots to standard CS2 behavior.!addtbot: Adds a new Terrorist bot to the game.!addctbot: Adds a new CT bot to the game.!removeallbots: Kicks all bots from the server.!help: Displays the available commands in chat.
These commands control who is allowed to use the bot management commands above. They are strictly limited to admins with the @css/generic flag.
!playersmanage: Updates configuration to ALLOW all players to use bot management commands.!adminsmanage: Updates configuration to RESTRICT bot management commands to Admins only.
The plugin uses a config.json file to persist permission settings.
- Default State: Players can manage bots (
PLAYERS_MANAGE_BOTS = true). - Restricted State: Only admins can manage bots (
PLAYERS_MANAGE_BOTS = false). - Help Message Visibility:
- Admins: Always see the full list of commands.
- Players (Allowed): See the bot management commands.
- Players (Restricted): Do not receive any help message or command feedback.
Basically, getting custom bot movement right in CS2 means fighting the engine a bit, but this setup handles the macro/micro handoff really smoothly. Feel free to use or modify the movement logic for your own PvE modes!