Page 1 of 1

quake2 code tidbits

Posted: Sun Jul 12, 2020 10:34 pm
by func_qbism
Q2 cd audio bug fix
Post by Barnes on insideqc

Some q2 engines (q2e, bers@q2) under win8.1 or high have bug in cd audio code - no realtime value change, code return error
To fix
in CDAudio_GetMixerVolume

Code: Select all

mxControlDetails.cbDetails = sizeof(mxValue);
and cange to

Code: Select all

mxControlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);

Re: quake2 code tidbits

Posted: Sun Jul 12, 2020 11:09 pm
by func_qbism
DirectQII with source by mh (Direct Q2)
(667.51 KiB) Downloaded 47 times

Re: quake2 code tidbits

Posted: Thu Jul 16, 2020 10:45 pm
by func_qbism
Team radio mod from an inside3d tutorial, might be interesting:

Code: Select all

Created By:	Achilles
Difficulty Scale:	Intermediate

Alright, what were going to do in this Tutorial is add a Team Radio feature. What this means is the players can now use pre-recorded .WAV files to send audio queues to eachother. Ranging from taunts, stupidity, to team based sounds.
I will make a couple of asumptions though.

You are familiar with your Compiler and its interface
You can find aspects of the code based on Function and not simply line numbers.
You are using the v3.20 source codebase from id Software
The first things we want to accomplish is add a functionality to turn the radio on and off. This is accomplished by adding a new value to the Player's info that survives a player respawn, i.e. we don't want it to reset everytime the player gets killed.

In g_local.h find the client_respawn_t structure, it should look somthing like this:
typedef struct
	client_persistant_t	coop_respawn;	// what to set client->pers to on a respawn
	int			enterframe;			// level.framenum the client entered the game
	int			score;			// frags, etc

	vec3_t		cmd_angles;			// angles sent over in the last command

	qboolean		spectator;			// client is a spectator
} client_respawn_t;
Underneath int score; you need to add our value for the Radio toggle.
// Inside3D Team Radio Tutorial - begin
	int			radio_power;    // Its a toggle that functions as the power
// Inside3D Team Radio Tutorial - end
Now we the data we need built into the player that allows the radio to be turned on and off. In the code it can now be accessed via the ent->client->resp.radio_power value. Since its a toggle it can be true or false.
Were all done with g_local.h

We now need to set the value of ent->client->resp.radio_power when the client connects.

Open up p_client.c

Your looking for the the ClientConnect Subroutine, the header for the routine looks like this:


Called when a player begins connecting to the server.
The game can refuse entrance to a client by returning false.
If the client is allowed, the connection process will continue
and eventually get to ClientBegin()
Changing levels will NOT cause this to be called again, but
loadgames will.
qboolean ClientConnect (edict_t *ent, char *userinfo)
	char	*value;

	// check to see if they are on the banned IP list
	value = Info_ValueForKey (userinfo, "ip");
	if (SV_FilterPacket(value)) {
		Info_SetValueForKey(userinfo, "rejmsg", "Banned.");
		return false;

What we need to do is find where exactly in this routine the value can be set. It just so happen I did this for you.
Directly underneath the following If statement contained in the ClientConnect function.

	if (ent->inuse == false)
		InitClientResp (ent->client);
		if (!game.autosaved || !ent->client->pers.weapon)
			InitClientPersistant (ent->client);
Simply add the following
// Inside3D Team Radio Tutorial - begin
	ent->client->resp.radio_power = 1;
// Inside3D Team Radio Tutorial - end
For a quick check of your code, go ahead and compile it now, no actual differences were made that can be used yet, but you should get a nice clean compile, if not re-check your steps. Now go grab a Coke, Pepsi or Mountain Dew. Nice work!
Now is when the tutorial gets a bit tricky, we need to add two new files to the Project. This is done slightly different in every compiler out there. Simply add both files and make sure they contained in the make file.

The files we want to add is called p_radio.c and p_radio.h

p_radio.h containes the following

// These are functions for the team radio

#define for_each_player(PLAYER,INDEX)				\
for(INDEX=1;INDEX<=maxclients->value;INDEX++)			\
	if ((PLAYER=&g_edicts[i]) && PLAYER->inuse)

void RadioToggle_f(edict_t *self);

void Radio_f(edict_t *self, char *channel, char *msg);
That file contains our prototypes for all our radio functions so we can call them up easily.
Now the big file, p_radio.c contains the follow:

#include "g_local.h"
#include "p_radio.h"

void RadioToggle_f(edict_t *self)
	if (self->client->resp.radio_power)
		self->client->resp.radio_power = 0;
		gi.cprintf(self, PRINT_HIGH, "Radio OFF\n");
		self->client->resp.radio_power = 1;
		gi.cprintf(self, PRINT_HIGH, "Radio ON\n");

// this functions is used to send commands to the clients.
void stuffcmd(edict_t *ent, char *s) 	
   	gi.WriteByte (11);	        
	gi.WriteString (s);
	gi.unicast (ent, true);	

void Radio_f(edict_t *self, char *channel, char *msg)
	edict_t	*player;
	int		i;
	char	*cmd, *pos;
	char	*prefix;
	char	*info;

	cmd = "\0";

      // if the players radio is off, lets not bother doing anything
	if (!self->client->resp.radio_power)

	// Well this is where we check the model to see what sound to send out.
	// Based this check from the IsFemale() Subroutine

	info = Info_ValueForKey (self->client->pers.userinfo, "skin");
	if (info[0] == 'f' || info[0] == 'F')
		prefix = "fem_";
	else if (info[0] == 'c' || info[0] == 'C')
		if (info[1] == 'y' || info[1] == 'Y')
			prefix = "cyb_";
		if (info[1] == 'r' || info[1] == 'R')
			prefix = "crk_";
      // If its an unidentifiable model, use the male sounds by default
		prefix = "male_";

	// Okay gotta make sure people can't stuff extra commands down to kill someone.
	// This is also why i send everything to the guy that sent it originally also
	if (pos = strstr(msg,";"))
		pos[0] = 0;
      // If the sound is bound for all players in the game this is what is executed
      // .WAVs are stored in sound/world/*.wav
	if (Q_stricmp (channel, "ALL") == 0)
		for_each_player(player, i)
			if (player->client->resp.radio_power)
				sprintf(cmd, "play world/%s%s\n", prefix, msg);
				stuffcmd(player, cmd);
      // If it's only based for players nearby AKA the player is yelling to people in game this is ran.
      // .WAVs are stored in sound/voice/*.wav
	else if (Q_stricmp (channel, "ROOM") == 0)
		sprintf(cmd, "voice/%s%s.wav", prefix, msg);
		gi.sound (self, CHAN_VOICE, gi.soundindex(cmd), 1, ATTN_NORM, 0);
Radio_Toggle_F is simply a toggle function, when it is called it changes the value of ent->client->resp.radio_power
Radio_F is commented heavily and should be fairly self-explanatory.

Now again lets see if the code will compile cleanly. All the functions are there and ready to go, just nothing is being called. This is a good place to check to see if the files were added to the project correctly and so forth. We'll get into the actual usage in a moment, right now, lets see if what we have so far is good and clean.

I am assuming since your going on everything compiled and the new files are being included in the compile.

Basically what happens is when a sound is played, the Radio_F routine check to find all players that should receive the sound. All players that are receiving the sound are sent a command that is executed on the local console. Example
play male_buzzoff.wav
It simply plays the .WAV to the speakers. Because I am using the players console, there is a check to prevent other things from being sent like Kill and quit commands.
Now the last part of our tutorial, adding the command so these routines actually get used.

This is done in g_cmds.c

At the bottom of the file in the ClientCommand Subroutine you need to add some code after these lines

	else if (Q_stricmp(cmd, "playerlist") == 0)
you need to add the following lines
// Inside3D Team Radio Tutorial - begin
	else if (Q_stricmp (cmd, "radio") == 0)  // Radio Toggle
	else if (Q_stricmp (cmd, "play_world") == 0 && !ent->client->resp.spectator)  // Radio to everybody
			Radio_f(ent, "ALL", gi.argv(1));
	else if (Q_stricmp (cmd, "play_voice") == 0 && !ent->client->resp.spectator)  // Talk to Everyone within a Earshot
			Radio_f(ent, "ROOM", gi.argv(1));
// Inside3D Team Radio Tutorial - end
this adds a couple extra command for the players to bind.
RADIO all by itself will toggle the radio on and off. Off simply means he will not hear any of the sounds sent by other players.

play_world command will play a command to every player in the game with the radio on. Command in reality is part of the name in the WAV file. IE play_world buzzoff wil actually play a file called male_buzzoff.wav etc...

play_voice is identical to play_word, except it only players to players in close proximity to the sender.

The !ent->client->resp.spectator is a check so Spectators to a game cannot send audio commands to the players in game.

Re: quake2 code tidbits

Posted: Thu Jul 16, 2020 11:01 pm
by func_qbism
Original Q2 qdata scripts

Re: quake2 code tidbits

Posted: Thu Jul 16, 2020 11:35 pm
by func_qbism
Quake 2 tech mini-library:

FAQ. In-depth when Q2 was new

Source Code Review

Game DLL Documentation ... Fq2doc.doc

Cinematics Uncovered ... cle02.html