That's still on a 2d grid with no holes the bots can't fit through. A more varied environment would be extremely hard to code for.
Is it possible to do? Yes.
Is it simple/easy to make? Not at all.
Even big games with actual budgets and teams use premade nodes that the AI is programmed to move to, simplifying pathfinding greatly.
So make your own nodegrids. Have nodes which are connected to other nodes, with path instructions - if the bot arrives at this node and wants to go to a specific other node, you might tell the bot to crouch before proceeding, to take the vent. Tell it to jump at this node to jump up onto some crates. Tell it to face a certain direction if this was its final destination, tell it to change its team, tell it to explode.
Then, you make node bricks, which when placed are placed rendering and solid; wrench a node brick, it is highlighted, wrench another to connect or disconnect them or wrench an unrelated brick to cancel. When you stand on a node, it automatically highlights all nodes connected to it, and then using a command (/finalizenodegrid) will index the grid to make searching it more efficient, and also make all the bricks invisible. When loading the save is complete, this command should be called automatically if any node bricks were placed by the loading process. Looking at a highlighted node and using /editpath or some similar command should let you change path instructions. Hell, make it so that when you stand on a node, you look at another node, and type:
/connect to connect them
/disconnect to disconnect them
/edit to edit path instructions/destination instructions for the current node if you're not looking at another node brick directly
/finalize to finalize this brick, cache it in the nodegrid and make it invisible/disable the highlighting behaviour, so that you can easily tell what nodes need to be worked on
/cancel when standing on/in the zone of a finalized brick to 'un-finalize' it, thus re-enabling its properties and the highlighting behaviour ('debug behaviours'?)
Then the bots themselves have a
goToNode function, which will find the nearest node to them then expand from this node recursively to find the node you told them to go to. If multiple nodes exist for that name, it chooses the nearest one, so telling them to go to "Machinegun" would tell them to go to the nearest (not beeline-nearest, shortest total distance) node by this name, assumedly the nearest node next to a HMG which would then mount them to the HMG in question when they arrive.
Then you also have a
goToPosition function which will find the nearest node to that position with a straight line to it, and call goToNode on it. If it already has a path cached to go to a position, it will check and see if its current destination node has a path to the new position, so it doesn't need to re-run the nodegrid to find its way in this case, and if it does, it will then re-path.
The issue with this is it's a lot of work to make
and to use. Not to mention debugging a forgeted nodegrid. Eugh.