void() army_run2 =[ $run2, army_run3 ] {ai_run(15);};
expands to:
void() army_run2 =
{
//this is the 'op_state' part from the square brackets
self.frame = $run2; //frame macros expand to immediates (read: constants). Each name defined by the "$frame" directive at the top of the qc file defines a $yourconstant that has 1 value more than the previous one, starting at 0. The qcc expands them to float immediates when it encounters them.
self.think = army_run3; //the named function from op_state is automatically prototyped as returning void and taking no arguments.
self.nextthink = time + 0.1; //op_state implies a specific 0.1 second interval. you can always override after.
ai_run(15); //yup, that's a regular function call.
};
the ai_run function executes the ai code for the 'run' behaviour. 'run' basically means running at the monster's .enemy and probably switching to attack animations randomly.
the argument is the number of quake units to move forward by. remember that its specified at 0.1 intervals, so 15 is effectively 150qu per second, but only for a 10th of a second...
per-frame values allow a lurching sort of animation, which nicely ties to the monster's lurching animation.
there's other ai functions like ai_stand() and ai_walk(x) for other states also there's ai_pain to step around. your attack states should probably update the angle the monster is facing, but they typically don't bother running any other ai while shooting.
side note: more modern animation styles for player-type characters would use a constant value for each frame, and have the model itself provide the staggering. you can then have the clientside gamecode update the animation at a rate relative to the actual distance traveled. this covers lag and all sorts of other issues, ensuring foot-sync. of course, if you're using vanilla content, your code needs vanilla-compatible lurches. yay!... *cough*. it doesn't matter too much with monsters, its players that can strafe and do other non-axial movements that make life fun that really need it.