• Register
Post tutorial Report RSS Quake c - be impulsive

Details of the quake-c interface with a human

Posted by on - Intermediate Server Side Coding

Quake-c has three distinct ways to know what a human player wants.

1. mouse directional movement (interpreted movement of the mouse, typically across the x-y plane of the monitor with a cursor, but used differently in FPS games)

// defs.qc
.vector angles;

2. button press on the mouse

// defs.qc
.float button0; // fire
.float button1; // use
.float button2; // jump

// Cataboligne - 9.30.11 - extended mouse button support from darkplaces (... and possibly other engines)
// some mice have a lot of buttons now: Cyborggaming.com (yes, I already have one...it is very nice)

.float button3, button4, button5, button6, button7, button8, button9, button10, button11, button12, button13, button14, button15, button16;

3. impulse command

// defs.qc
.float impulse; // weapon changes

Impulse is a console command - "impulse {number}" where {number} is an integer value 1 - 255.

impulse 2; // select weapon slot 2 - shotgun

Of course, having to pull down the console to issue commands would be an extreme challenge in game control, so a number of shortcut possibilities are used.
The most common type is another console command called bind - "bind {keypress} {command sequence}"

// typical weapon slot key bindings
// note that keys can be bound to multiple commands - either by a semicolon separated list or with aliases

bind "1" "impulse 1"
bind "2" "impulse 2"
bind "3" "impulse 3"
bind "4" "impulse 4"
bind "5" "impulse 5"
bind "6" "impulse 6"
bind "7" "impulse 7"

When the indicated key is pressed, the assigned command is entered directly in the console.

Usage in code.

Mouse movement:

The mouse directional interpretation is assigned by the engine to the player entity vector ".angles".
The player entity is set by the engine from a reserved list of entity slots (usually 1 - 16) upon player network connect.
(Debugging hint: for singleplayer and the player hosting a "-listen" server [ not a "-dedicated" server ] this will always be entity 1.)

How is mouse movement interpreted by the engine?

Movement up and down (often thought of as the y plane on your monitor) is interpreted as a scalar rate and used to change angles_x referred to as "pitch".
(Debugging hint - pitch is reversed from expectations and limited - this is NOT a viewpoint angle - looking up has positive angles_x and looking down has negative angles_x, with a range of 30 to -30. So when the player looks straight up - 90 degrees from viewpoint of 0 degrees or straight ahead, the value of angles_x is 30.)

This causes player viewpoint to move up and down - from pointing straight up to pointing straight down. Additionally any model assigned to the player entity will be adjusted by using the angles_x value as an actual angle. What this means is when a player looks up, the model will "tilt" back at angles_x degrees. When the player looks down the model will "tilt" forward at angles_x degrees .

As if this usage of pitch was not already confusing enough - for a visual understanding this video segment demonstrates first pitch (up-down), then yaw (side to side) and then faster pitch / faster yaw:

fig 1. pitch and yaw

Movement from side to side (often thought of as the x plane on your monitor ) is interpreted as a scalar rate and used to change angles_y referred to as "yaw".
This is an actual facing angle that represents the players view offset from angle 0. Any model assigned to the player entity will be rotated to the same angle.

The ".angles" vector values are modified by a rate - the faster you move the mouse the faster they change. This degree of rate control is the primary reason some keyboard + mouse users despise console controls. (Even the silly little analog sticks that attempt to do the same thing.)

You can also assign pitch and yaw angles in quake-c. This code fragment forces the engine to change the viewpoint to the assigned value:

// teleporters use this to point the player in the proper direction

// self must be the player entity - often it will be other in the case of touch with trigger entities

self.fixangle = TRUE; // turn this way immediately

Mouse buttons:

The button values are read by the engine from the OS drivers and the floats defined above are assigned the set values.

For each mouse button pressed, the corresponding .button{n} value is set to 1 - if that button is not pressed it is 0.

While all mouse button presses could be seen per frame - you can only see one impulse value in quake-c per frame!

The mouse button values can be read anywhere in the code until they are zeroed. This is typically done in functions called from PlayerPreThink() and PlayerPostThink() ( per frame code: Moddb.com )

Impulse commands:

The float ".impulse" is assigned the value of an impulse command by the engine when one is entered in the console by that player - either directly, by key binding or alias.

Impulse commands are handled in client.qc and weapons.qc.
Found in PlayerPostThink() covered here: Moddb.com

// do weapon stuff - after intermission and dead checks

W_WeaponFrame ();

// in weapons.qc

void () W_WeaponFrame =
if (time < self.attack_finished)

if (self.impulse)
ImpulseCommands(); // also in weapons.qc

void () ImpulseCommands =
// weapons are changed 1 - 7
// cycle weapons (using key press or mousewheel / buttons) are done

// *** any mod impulses should be checked and dealt with here

// *** sample code - this is an example only!

if (self.impulse == 66)
bprint("developer: Impulse 66 detected !\n");

if (self.button6)
bprint("developer: Mouse button #6 detected !\n");
bprint(" player facing angle: ");
bprint(" - pitch: ");
bprint(" - pitch view angle: ");
bprint(ftos(self.angles_x * -3)); // correct for sign, estimate true view angle
// *** end sample code

// MUST be cleared for next impulse and so command is not repeated
self.impulse = 0;

Your best opportunity to handle impulse commands is in this function *** before the value is zeroed.

Player interface is done in the per frame code segment via PlayerPreThink() and PlayerPostThink() ( per frame code: Moddb.com ) to interpret commands as quickly as possible.

New impulses can be added for: bots, admin control, alternate weapon modes, grapple hooks, dropping runes or items, and many other exciting details.

Good luck adding new impulses to your mods!

numbersix Author

Example code notes:

I tried the sample code. When a command is bound to MOUSE6 it runs.
When you try to detect button6 in the quake-c code it does not seem to work.
Buttons 3+ may or may not be passed on to quake-c!

You can still bind impulses and other commands to mouse buttons...

I may report back on this if I get a chance to look at the related engine code.

The impulse sample:

]impulse 66
developer: Impulse 66 detected !

Works just fine.

Reply Good karma+2 votes
numbersix Author

Notes for inline video on pitch and yaw:

the pitch control (value of angles_x) rotates the model around its engine "seen" 0 value for the y axis. The same applies to the yaw control (angles_y) rotation around z axis 0 value.

You can see this in the video - pitch rotates around a point about 24 units up from the floor.

Thus if you have a model with a (0, 0, 0) origin at its feet, the pitch rotation will happen from the feet...or if the origin is not centered the yaw rotation will appear strange - rotating from a corner of the model.

Reply Good karma+1 vote
numbersix Author

On detecting mouse buttons - apparently you must do this in a config file:

bind mouse3 +button4
bind mouse4 +button5
bind mouse5 +button6
bind mouse6 +button7
bind mouse7 +button8
bind mouse8 +button9
bind mouse9 +button10

And this worked in my test:

developer: Mouse button #6 detected !
player facing angle: -40.940552 - pitch: 5.573730 - pitch view angle: -16.721191

You might have to experiment with which mouse{number} corresponds to the actual mouse button.

Reply Good karma+2 votes

Great info !

Also worth mention, a real clients angles is a direct representation of the position of the mouse, when using the mouse (+mlook). The field its passed to is the clients v_angle. While you can alter the players speed for running or walking, you cannot manipulate its angle_speed as its a direct feed from the client. SO lets say you are in water or slime - you should really rotate at a slower angle when looking around, but this is a basic Quake shortcoming. I have had some success intercepting the v_angle field in multiplayer, and using changeyaw () to make the player look like its yawing slower when waterlevel > 2, but the client side will always be yawing at the same speed, which will wind up being inconsistent for gameplay purposes, but an interesting experiment.

Also, I found a way to detect the player moving the mouse using QC. Darkplaces .movement vector can detect keys movement, but not mouseangles.

// In player prethink add this after declaring a .mouse_move float in defs.qc
if (!(framecount & 1))
// do this only every other frame to save resources
if ((clienttype(self) == CLIENTTYPE_REAL))
self.mouse_move = (vlen(self.angles));

// Add this in playerpostthink
if (framecount & 1)
if ((clienttype(self) == CLIENTTYPE_REAL))
if (self.mouse_move != vlen(self.angles))
bprint ("Im moving the mouse\n");

Reply Good karma Bad karma+1 vote
Post a comment
Sign in or join with:

Only registered members can share their thoughts. So come on! Join the community today (totally free - or sign in with your social account on the right) and join in the conversation.

Related Engines
DarkPlaces engine
DarkPlaces engine GPL
Quake Engine
Quake Engine GPL
Related Groups
Dark Places engine team
Dark Places engine team Developer with 15 members
id Software
id Software Developer & Publisher
QuakeDB Fans & Clans with 212 members