QuakeC Code Snippets

Discuss programming in the QuakeC language.
GiffE
Posts: 170
Joined: Sun Oct 08, 2006 3:39 pm
Location: USA, CT
Contact:

QuakeC Code Snippets

Post by GiffE »

I'd like to start a thread where we can all post small code snippets which may be of use to others learning quakec or just save some time.
Theres a lot of little things we code which seem redundant as someone has likely done it before. You can spend a lot of time reinventing the wheel and less time working on the new fun things.
I will make a page on DP Wiki and update it with these if we see fit.

I'll start it off:
This method will play a random sound. I find I have to do this alot so why not make a method for this.

Code: Select all

// Spike:
string(string fmt, string sub1) format_s = 
{ 
    float p; 
    p = strstrofs(fmt, "%s", 0); 
    if (p == -1) 
        return fmt; 
    return strcat(substring(fmt, 0, p), sub1, substring(fmt, p+2, -1)); 
}; 

/* 	randomSound
	Same as sound() except takes a float cnt.
	plays a random variant of the sound.  for example: 
		randomSound(self,CHAN_BODY,"Sounds/Player/pl_step%s.wav",0.3,ATTN_IDLE,3);
	plays randomly Sounds/Player/pl_step1.wav, 
				Sounds/Player/pl_step2.wav, or 
				Sounds/Player/pl_step3.wav
				
	EXT used:
		FRIK_FILE
		FTE_STRINGS
*/
void(entity e, float chan, string samp, float vol, float atten, float cnt) randomSound = {
	float r;
	r = floor(random() * cnt) + 1;
	sound(e, chan, format_s(samp,ftos(r)), vol, atten);
};
Extensions used:
FRIK_FILE, FTE_STRING
Last edited by GiffE on Fri May 21, 2010 1:04 pm, edited 3 times in total.
Mexicouger
Posts: 514
Joined: Sat May 01, 2010 10:12 pm
Contact:

Post by Mexicouger »

That's alot different than How I do my random sounds.
In the function, I make a local float r;
and then make
r= random();

then do this some where in the function:

if (r >= 0.33)
sound (self, CHAN_WEAPON, "player/beffy.wav", 1, ATTN_NORM);
else if (r >= 0.66)
sound (self, CHAN_WEAPON, "player/beffy2.wav", 1, ATTN_NORM);
else
sound (self, CHAN_WEAPON, "player/beffy3.wav", 1, ATTN_NORM);

That is how I make my random sounds. So basically.

void() myfunc =
{
local float r;
r = random();

if (r >= 0.33)
sound (self, CHAN_WEAPON, "player/beffy.wav", 1, ATTN_NORM);
else if (r >= 0.66)
sound (self, CHAN_WEAPON, "player/beffy2.wav", 1, ATTN_NORM);
else
sound (self, CHAN_WEAPON, "player/beffy3.wav", 1, ATTN_NORM);

};
That could be a function all in itself.

And then goto your real function and replace the sound with;

myfunc();

Kinda scattered, But That is it!
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Post by Spike »

Code: Select all

if (r < 0.33)
    blah()
else if (r < 0.66)
    blah();
else
    blah();
would work better.
your code will use the first case 2/3rds of the time, and never the middle case.

instead, if you prefer single-lines and use frikqcc or fteqcc, you could write it as:
r=random();
sound (self, CHAN_WEAPON, ((r<0.33)?"player/beffy.wav":((r<0.66)?"player/beffy2.wav":"player/beffy3.wav")), 1, ATTN_NORM);
but that's the ?: ternary operator for you - unreadable.
GiffE
Posts: 170
Joined: Sun Oct 08, 2006 3:39 pm
Location: USA, CT
Contact:

Post by GiffE »

Spike wrote:

Code: Select all

if (r < 0.33)
    blah()
else if (r < 0.66)
    blah();
else
    blah();
would work better.
your code will use the first case 2/3rds of the time, and never the middle case.

instead, if you prefer single-lines and use frikqcc or fteqcc, you could write it as:
r=random();
sound (self, CHAN_WEAPON, ((r<0.33)?"player/beffy.wav":((r<0.66)?"player/beffy2.wav":"player/beffy3.wav")), 1, ATTN_NORM);
but that's the ?: ternary operator for you - unreadable.
Really? I was always told by all my cs professors that with a random number function that returns from 0.0 to 0.9999999, you can do:
floor(random() * 3) + 0; where 3 = the number of numbers it can fall between, and 0 = the starting value.

0.3333 * 3 = 0.9999 so floor it = 0
0.6666 * 3 = 1.9999 floor it = 1
0.9999 * 3 = 2.9997 floor it = 2

Any value between these produce the same result due to the floor.
And by adding anything it simply shifts the results so I can effectively choose any starting value, in my case 1.
Test it:

Code: Select all

float zeros;
			float ones;
			float twos;
			float i;
			float r;
			zeros = ones = twos = 0;
			for(i=0;i<1000;i++) {
				r = floor(random() * 3);
				switch(r) {
					case 0: zeros++; break;
					case 1: ones++; break;
					case 2: twos++; break;
				}
			}
			print("0: ", ftos(zeros), " (", ftos(zeros/i),")\n");
			print("1: ", ftos(ones), " (", ftos(ones/i),")\n");
			print("2: ", ftos(twos), " (", ftos(twos/i),")\n");
This produces the result of:
0: 341 (0.341000)
1: 324 (0.324000)
2: 335 (0.335000)
And i'd say that is damn close to 1 third.

I use this method instead of all the if/else's because it allows me to pass in any number of sound files to choose from, so if there's 4, 10, 100 different sounds for the same one it will always work.

EDIT:
I had believed Spikes post was directed towards me, which it clearly wasn't. Miscommunication on my behalf, however there's my attempt at explaining random numbers using the method I use...
Last edited by GiffE on Fri May 21, 2010 2:00 am, edited 1 time in total.
Sajt
Posts: 1215
Joined: Sat Oct 16, 2004 3:39 am

Post by Sajt »

This:

Code: Select all

local float r;

r = random();

if (r < 0.33)
    a;
else if (r < 0.66)
    b;
else
    c;
is slightly briefer and more readable than this:

Code: Select all

local float r;

r = floor(random() * 3);

if (r == 0)
    a;
else if (r == 1)
    b;
else
    c;
(This latter method is usually only used if you have 5+ cases.) Not that I'm saying it's wrong. Probably 50% of QC coders do do it this way all the time. But the first method is no less fine. edit: And people don't always use switch statements because they're not a standard QCC feature (I think only FTEQCC supports them) and you can't always trust FTEQCC to work with non-standard features :) (although I think switch statements are fine).

Also, I need to react to the enormous puke of code in your first post. What you did there is fun for the sake of it, but a complete waste of time to the practical coder and also comparatively very hard to understand.
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.
GiffE
Posts: 170
Joined: Sun Oct 08, 2006 3:39 pm
Location: USA, CT
Contact:

Post by GiffE »

Sajt wrote:This:

Code: Select all

(This latter method is usually only used if you have 5+ cases.) Not that I'm saying it's wrong. Probably 50% of QC coders do do it this way all the time. But the first method is no less fine. edit: And people don't always use switch statements because they're not a standard QCC feature (I think only FTEQCC supports them) and you can't always trust FTEQCC to work with non-standard features :) (although I think switch statements are fine).

Also, I need to react to the enormous puke of code in your first post. What you did there is fun for the sake of it, but a complete waste of time to the practical coder and also comparatively very hard to understand.[/quote]

Ouch,
I had not used a switch in my first post, only the second for the sake of testing.  I never said one is better than the other.  It also comes down to ones unique coding style whether one uses random()*3 or just uses the decimal, however for the method I wrote its more functional to use the first.  I don't know what you mean by stuff just thrown in there, every line of it is needed.. other than possibly cutting down on the ext variable.  
I did forget to hack off the else/returns (which I forgot to get rid of when I altered it slightly during posting).  I removed them if it makes your eyes less sore... 

I made this thread thinking you all had written small code bits, too small for a tutorial or posting on their own, but useful none the less.  While they may be novelty to some, they could be fun to see, and learn how another person approaches a "problem".
Forgive me if my post sounds defensive...
Pulseczar
Posts: 37
Joined: Sat Aug 12, 2006 6:45 pm

Post by Pulseczar »

Sajt wrote:Also, I need to react to the enormous puke of code in your first post.
Don't hold back, man. Tell him how you really feel...

And yeah, the logic is wrong in Mexicouger's code.
Sajt
Posts: 1215
Joined: Sat Oct 16, 2004 3:39 am

Post by Sajt »

Well, GiffE, I should mention that I was the same way when I was "your age" (-ish). I didn't finish any mods because I was too busy making cool code, I guess. Anyway, to be more helpful, the first thing I would do to make randomSound clearer would probably be to make the input "filename" a printf-like pattern, like "Sounds/Player/pl_step%d.wav". Although I don't know if DP has builtins for handling that (you could always write a ton of QC code to do it!)

Hmm, sorry if I'm derailing the thread a little bit. As for cool snippets of QC, I don't really have any that are small and independent that I could post without having to explain a half-dozen other things it's interconnected with...
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Post by Spike »

Code: Select all

string(string fmt, string sub1) format_s =
{
    float p;
    p = strstrofs(fmt, "%s", 0);
    if (p == -1)
        return fmt;
    return strcat(substring(fmt, 0, p), sub1, substring(fmt, p+2, -1));
};
Just finds %s in the string and substitutes that part with the second argument. Requires FTE_STRINGS. Also requires bugfixed qcc, and some kind of multiple tempstrings
GiffE
Posts: 170
Joined: Sun Oct 08, 2006 3:39 pm
Location: USA, CT
Contact:

Post by GiffE »

Spike wrote:

Code: Select all

string(string fmt, string sub1) format_s =
{
    float p;
    p = strstrofs(fmt, "%s", 0);
    if (p == -1)
        return fmt;
    return strcat(substring(fmt, 0, p), sub1, substring(fmt, p+2, -1));
};
Just finds %s in the string and substitutes that part with the second argument. Requires FTE_STRINGS. Also requires bugfixed qcc, and some kind of multiple tempstrings
Very cool! Didn't think of that :D! I was so focused on just parsing the string and adding a number, a simple replace makes it much easier:

Code: Select all

void(entity e, float chan, string samp, float vol, float atten, float cnt) randomSound = {
	float r;
	r = floor(random() * cnt) + 1;
	sound(e, chan, format_s(samp,ftos(r)), vol, atten);
};
Thanks Spike!
Mexicouger
Posts: 514
Joined: Sat May 01, 2010 10:12 pm
Contact:

Post by Mexicouger »

[/quote]
And yeah, the logic is wrong in Mexicouger's code.[/quote]

...I don't quite understand how my "logic" was wrong. The code works just fine. It is just a different way of selecting a random sound effect for a function.
Sajt
Posts: 1215
Joined: Sat Oct 16, 2004 3:39 am

Post by Sajt »

Mexicouger wrote:

Code: Select all

if (r >= 0.33)
    ...;
else if (r >= 0.66)
    ...;
else
    ...;
The first clause has a 66% chance of occurring, the second clause 0%, and the third 33%. If you replaced ">=" with "<=", however...
F. A. Špork, an enlightened nobleman and a great patron of art, had a stately Baroque spa complex built on the banks of the River Labe.
Pulseczar
Posts: 37
Joined: Sat Aug 12, 2006 6:45 pm

Post by Pulseczar »

Mexicouger wrote:...I don't quite understand how my "logic" was wrong. The code works just fine. It is just a different way of selecting a random sound effect for a function.
Which path should be taken if r = 0.9? The second, but your code takes the first. Since code runs sequentially, it always checks the first if first, and for all values >= 0.33, it executes that first if clause.
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

Pulseczar wrote:
Mexicouger wrote:...I don't quite understand how my "logic" was wrong. The code works just fine. It is just a different way of selecting a random sound effect for a function.
Which path should be taken if r = 0.9? The second, but your code takes the first. Since code runs sequentially, it always checks the first if first, and for all values >= 0.33, it executes that first if clause.
Mexicouger,

Code: Select all

if (r > 0.66) // if r between 1.00 and 0.67
sound (self, CHAN_WEAPON, "player/beffy3.wav", 1, ATTN_NORM);
else if (r > 0.33) // if r between 0.66 and 0.34
sound (self, CHAN_WEAPON, "player/beffy2.wav", 1, ATTN_NORM);
else // if r between 0.33 and 0.00
sound (self, CHAN_WEAPON, "player/beffy.wav", 1, ATTN_NORM); 
That's what your code should look to have the same chance to play all of the 3 sounds. You may not realized the error because your code always plays some sound - it's just the wrong one.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
GiffE
Posts: 170
Joined: Sun Oct 08, 2006 3:39 pm
Location: USA, CT
Contact:

Post by GiffE »

I was playing around with the dp's file searching extensions (DP_QC_FS_SEARCH and FTE_STRINGS) a few days back. I noticed there was a random level tutorial in the qc tutorials so I figured's I'd update it with DP's extensions:

Code: Select all

string() RandomMap = {
	string rmap;
	float handle,r;
	handle = search_begin("maps/*.bsp", FALSE, TRUE); //Find all Maps
	r = floor(random() * search_getsize(handle)); // Choose a random index
	rmap = search_getfilename(handle,r); // Get our map
	search_end(handle);
	return substring(rmap,5,strlen(rmap)-9); // Strip maps/ and .bsp (changelevel doesn't like those)
};
It searches ALL BSP's so if you have bsp in the maps directory that isn't a map it could return it... which you may have to find an a way to fix it if you use bsp's as anything but maps.

This can be used to replace the Random Level Changing Tutorial.
Getting rid of this ugliness:

Code: Select all

local float whichmap;
	
	whichmap = rint (random()*36 + 1);
	
	if (whichmap == 1)
		mapname = "start";
	else if (whichmap == 2)
		mapname = "e1m1";
...
	else
		mapname = "dm6";
I used it in combination with csqc to create a Halo: Reach style "next map vote" that I took a liking to while playing the beta:
Image
I might try to see if I can use "WritePicture" to send a small thumbnail for each map.
Post Reply