Snippet

Discuss programming in the QuakeC language.
behind_you
Posts: 237
Joined: Sat Feb 05, 2011 6:57 am
Location: Tripoli, Libya

Snippet

Post by behind_you »

I'm willing to try to restart a snippet forum. Hopefully people will post their small qc codes. Any contribution is helpful. If no one wants to contribute, I'll just keep posting stuff.

Anywho, my first contribution. This code lets you draw an empty box of any thickness for use in menuqc or csqc. making inorout 0 makes the box drawn inside where you want it. make it 1 for it to be drawn outside and 2 to make it draw around the center of where you want it. any of you who use photoshop will know what I'm talking about

Code: Select all

void(vector pos, vector length, float thickness, vector boxcolor, float boxalpha, float boxflag, float inorout) drawemptybox = 
{
	local vector thickv;
	local vector top, left, posdown, posright, poss;
	
	if (!inorout)
		thickv_x = 0;
	else if (inorout == 1)
		thickv_x = thickness;
	else if (inorout == 2)
		thickv_x = thickness / 2;
	
	thickv_y = thickv_x;

	pos = pos - thickv;
	length = length + thickv;
	
	top_x = thickness;
	top_y = length_y - thickness;
	
	left_x = length_x + thickness;
	left_y = thickness;
	
	posright_x = pos_x + length_x;
	posright_y = pos_y + thickness;
	
	posdown_x = pos_x;
	posdown_y = pos_y + length_y;
	
	poss_x = pos_x;
	poss_y = pos_y + thickness;
	
	drawfill(poss, top, boxcolor, boxalpha, boxflag);		//|
	drawfill(posright, top, boxcolor, boxalpha, boxflag); 	//  |
	drawfill(pos, left, boxcolor, boxalpha, boxflag);		// -
	drawfill(posdown, left, boxcolor, boxalpha, boxflag);	// _
	
};
behind_you
Posts: 237
Joined: Sat Feb 05, 2011 6:57 am
Location: Tripoli, Libya

Post by behind_you »

Ok, this code makes a traceline. that traceline will hit the wall in front of you, and change your angles to the wall's angles. AKA do this, then when you fire your weapon, the bullet will travel parallel to the wall. I'm using this code for AI navigation, but it can be put to other uses as well.

Code: Select all

vector(vector which) positivevec = //finds any negative numbers in the vector and makes it positive
{

	if (which_x < 0)
		which_x = which_x * -1;

	if (which_y < 0)
		which_y = which_y * -1;

	if (which_z < 0)
		which_z = which_z * -1;

	return which;
};

void() wallangles = 
{
	local vector tpn, anglechange, endpos;
	local float walldist, opt1dist, opt2dist;

	traceline (self.origin, self.origin + v_forward * 10000, TRUE, self);//find the wall you are looking at
	endpos = trace_endpos;//save this trace_endpos, since the other tracelines that will be made will overwrite it
	
	walldist = vlen(endpos - self.origin);//distance between you and wall, needed later
	
	tpn = positivevec(trace_plane_normal * 180);//gets the wall angle (out of 1) and multiplies it by 180(also makes sure no values are negative)

	tpn_x = tpn_x / 2;//divides x value b 2
	tpn_y = tpn_y * - 1;//changes sign of y value from positive to negative (or vice versa)

	// I really do not know why the above 2 lines are needed. i got them through trial and error :P.
	
	anglechange_y = tpn_x + tpn_y;//adds the x and y values of wall (this will translate to whatever angle the player will have
	
	makevectors(anglechange);//figures out the forward, up and right directions relative to the wall's angles
	
	traceline(self.origin + v_forward * walldist, self.origin + v_forward * walldist, MOVE_WORLDONLY, world);//don't interact
	opt1dist = vlen(trace_endpos - endpos);
	
	traceline(self.origin - v_forward * walldist, self.origin - v_forward * walldist, MOVE_WORLDONLY, world);//don't interact
	opt2dist = vlen(trace_endpos - endpos);
	
	//the 4 above lines creates 2 points. then it subtracts the distance of the points by the original point that was made with the first traceline (endpos). this is a hacky way to turn your angle to the nearest available option.
	
	if (opt1dist > opt2dist)
		anglechange_y = anglechange_y - 180;//if the way you plan on turning is bigger than the other way, use the other way instead by subtracting the desired angle by 180
	
	self.angles_y = anglechange_y;//you now face the same angles as the wall
	
	self.fixangle = TRUE;//set your angles, needed
};
behind_you
Posts: 237
Joined: Sat Feb 05, 2011 6:57 am
Location: Tripoli, Libya

Post by behind_you »

This is a simple code that lets you look directly at an entity:

Code: Select all

void(entity lookingat) faceentity = 
{
	local vector result, dir;

	result = lookingat.origin - self.origin;//subtract the entity origin you want to look at from your origin
	dir = normalize (result);//normalize the above: which means you figure out the direction of it

	self.v_angle = vectoangles (dir);//set your v_angle to face it

	self.angles_y = self.v_angle_y;
	self.angles_x = self.v_angle_x;//set your x and y angles to the result. don't do z angle because it will tilt your screen and you will end up with a dead quake angle effect :P
	
	self.fixangle = TRUE;//sets the angle, you look at entity :D
};
behind_you
Posts: 237
Joined: Sat Feb 05, 2011 6:57 am
Location: Tripoli, Libya

Post by behind_you »

With csqc, you can make your own HUD. This is a small noob explanation on how you can draw a string that represents your health. A bonus: Whenever your health reaches 25, it will pulse a red color! Neat, huh?


As usual, my code is heavily commented for your learning:

Code: Select all

//=================
vector healthcolor;
float healthtoggle;
//=================

//above is for pulsing health (a boolean float and the gradually changing vector(every pulsing item needs a separate set)

vector hpos;//position health text

float(float ccolor, float colortoggle, float minc, float maxc, float pulsespeed) valuepulse = //my nifty pulsing code (can be used to pulse anything, not just colors. can be used with alpha as well!)
{

	if (ccolor >= maxc)//if your color reaches the max allowed
		colortoggle = FALSE;//make the toggle false
	else if (ccolor <= minc)//if your color reaches the minimum allowed
		colortoggle = TRUE;//make toggle true

	if (colortoggle == TRUE)//if toggle is true
		ccolor = ccolor + pulsespeed;//increase color by desired speed
	else
		ccolor = ccolor - pulsespeed;//otherwise, decrease color by desired speed

	return ccolor;//give color value

}

void() DrawHealth = 
{
	float currenthealth;
	string healthstring;
	
	currenthealth = getstati(STAT_HEALTH);//gets your health value
	
	hpos_x = vid_size_x * 0.0150;
	hpos_y = vid_size_y * 0.8500;//positions health string
	
	healthcolor_x = 1;//set this to the color you want to pulse it to (x = red, y = green, z = blue). if you change, be sure you adjust the other healthcolor_'s ac

	if (currenthealth > 25)//if your health is above 25
		healthcolor_y = 1;//don't pulse
	else
		healthcolor_y = valuepulse(healthcolor_y, healthtoggle, 0.25, 1, 0.05);//else start pulsing. explanation on how to use:
	/*
	healthcolor_y: which float to pulse
	healthtoggle: which float to use as boolean(switch)
	0.25: lowest allowed number for float to reach before pulsing up
	1: highest allowed number for float to reach before pulsing down
	0.05: speed of pulse (higher number = faster pulse)
	*/

	healthcolor_z = healthcolor_y;//make sure the other value (z in this case) equals the pulsing value

	healthstring = ftos(currenthealth);//makes your health a string so you can print it.
	
	if (currenthealth > 0)//if you are alive
		drawstring(hpos, healthstring, '10 10 0', healthcolor, 0.75, 0);//draw your health string

}
Then in the View.qc file, in the function CSQC_UpdateView (maybe called something else in FTE), add this to the bottom of it:

Code: Select all

DrawHealth();
explaining how to drawstring:

look at this example: drawstring(where, what, howbig, whatcolor, howopaque, flag);

where: this is the position of the text(vector)
what: this is the text to write(string)
howbig: this is the size of text(vector)(example: '10 10 0', which is width x height). never mess with the last value and keep it a zero since this is the z value (depth). it is invalid here since the text is 2d.
whatcolor: this is the color of text(vector). in my code: if health is above 0, it's white. otherwise, it pulses red.
howopaque: alpha of text(float). AKA how transparent the text is. lower values = more transparent.
flag: this is a boolean flag(float). if its set to a number besides 0, your text's transparency will be affected by it's color vector. basically a text with it's color '0.4 0.1 0.2' will be more transparent than a text with it's color '1 0.7 0.5'

Enjoy! More posts coming soon!
behind_you
Posts: 237
Joined: Sat Feb 05, 2011 6:57 am
Location: Tripoli, Libya

Post by behind_you »

This small addition allows the character to walk slower whenever a certain button is held.

This is all in PlayerPreThink in client.qc:

Code: Select all

	local	float	maxspeed;
	
	maxspeed = cvar("sv_maxspeed");//find out your current speed

	if (self.button3)//if you are holding down the button (change this to any button you want)
		{
		if (maxspeed != 80)//if your speed is not equal to walking speed
			cvar_set ("sv_maxspeed", "80");//make it equal to walking speed
		}
	else //if you are not touching the button
		{
		if (maxspeed != 200)//if your speed is not equal to normal speed
			cvar_set ("sv_maxspeed", "200");//make it equal to normal speed
		}
In config.cfg, add this line:

Code: Select all

bind SHIFT "+button3"
Enjoy!
behind_you
Posts: 237
Joined: Sat Feb 05, 2011 6:57 am
Location: Tripoli, Libya

Post by behind_you »

Ok,

Let's say you have a number. You want to only get one digit from the number. The following function allows you to do this.

Note: If you are using vanilla QuakeC, you will need a qc file called mathlib.qc by FrikaC. Here it is

So, anywho here's the code (heavily commented for your learning):

Code: Select all

float(float num, float numpos) numsegment =
{
	local float factornum;

	if (numpos <= 0)
		numpos = 1;
		
	numpos = floor(numpos);
	//above lines to correct any mistakes made when you use the function

	factornum = 1 / pow(10, numpos); //if using mathlib.qc, rename pow to mathlib_pow
	
	//divide 1 by 10 to the power of whatever number position you want to find. (ie numpos = 1 gets a factornum = 0.1, numpos = 2 factornum = 0.01, etc.)

	num = num * factornum;//multiply the number by factornum found above(the number in the number pos you want should fall right of decimal point after this line)
	num = floor((floor(num) - num) * 10);//subtract a rounded version of above from regular version. this leaves you with only your desired number right after decimal point. then, multiply it by 10 to get your desired number in the ones position and round it to get rid of excess

	return num;//phew! Return the number for use.
};
To use this:
numsegment(number, numberpos);

number = the number you are using
numberpos = the position of the number you want

Example:

Let's say 'number' you are using is '5986'

If you use 1 as 'numberpos', the function will return '6'
If you use 2 as 'numberpos', the function will return '8'
If you use 3 as 'numberpos', the function will return '9'
If you use 4 as 'numberpos', the function will return '5'
If you use a number smaller than 1 as 'numberpos', the function will return '6' (AKA same as using 1)
If you use a number larger than the total amount of digits in the number as 'numberpos', the function will return '0'

Alright! Now here's my second contribution for the day:

Let's say you divide 2 floats. Sometimes it is useful to know if the two floats divide into each other evenly AKA no decimal point in answer. This function returns TRUE or FALSE depending on if the two numbers provided divide into each other evenly:

Code: Select all

float(float num1, float num2) evendivide =
{
	local float calc;
	
	calc = num1 / num2;//divide the numbers
	
	calc = (floor(calc) - calc) * 1000;//this basically checks if numbers exist after decimal point (ie looking for a number like 76.6785 or 82.0001 or 55.025 etc)
	
	if (calc)//if there is numbers after decimal point (AKA if the two numbers do not divide into each other evenly)
		return FALSE;//it's false
		
	return TRUE;//otherwise the numbers divide into each other evenly and it is true
};
And there you have it!
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Post by toneddu2000 »

Very useful section, thanks for sharing!
behind_you
Posts: 237
Joined: Sat Feb 05, 2011 6:57 am
Location: Tripoli, Libya

Post by behind_you »

Thank you, toneddu2000 for being the first person, besides me to reply to this topic!

Two more snippets :D

Today we will figure out whether any file you specify in qc exists or not. This is done by using the search functions which AFAIK, are supported in DP and probably FTE.

Here's the code:

Code: Select all

float(string nameandloc) fileexists =//returns TRUE if specified file exists
{
	local float findtext;
	local string foundtext;

	findtext = search_begin(nameandloc, FALSE, FALSE);//start searching for file. The first FALSE is for case-insensitivity. Make it true and all matching results will yield regardless of capital letters
	foundtext = search_getfilename(findtext, 0);//find the file name of the first file you find
	search_end(findtext);//end the search. No longer needed

	if (foundtext == nameandloc)
		return TRUE;//if file with same name found, return true

	return FALSE;//otherwise, return false (file not found)
};
To use it, assign the value of a float to this:

fileexists(NAMEHERE);

Note that you have to include the path to the file as well as the filename.

Example:

If you want to check if a certain picture called 'overhere.tga' exists in the folder 'gfx/thisway', you must supply the following in place of NAMEHERE:

"gfx/thisway/overhere.tga"

This is the only way it will work.

===

The simple function here will round a number to either of the two numbers provided, depending on the which of the 2 numbers it's closest to.

Code: Select all

float(float num, float small, float big) roundto =
{
	local float smalldist, bigdist;
	
	smalldist = num - small;
	if (smalldist < 0)
		smalldist = smalldist * -1;//subtract the number from the first possible value and make sure answer is positive

	bigdist = big - num;
	if (bigdist < 0)
		bigdist = bigdist * -1;//subtract the number from the second possible value and make sure answer is positive
	
	if (bigdist > smalldist)
		return smalldist;
	else if (smalldist > bigdist)
		return bigdist;//these lines check which of the saved numbers earlier is bigger. AKA which number is closest to the number you are rounding. It returns the value that is closest
	else
		return num;//if both values are the same distance from the number, return the unmodified number (some may want to do something else in this case)

};
To use:

Assign a float to this: roundto(number, value1, value2);

Number is the number that is rounding
Value1 and value2 are the values that the number will be rounding to.

Enjoy!
behind_you
Posts: 237
Joined: Sat Feb 05, 2011 6:57 am
Location: Tripoli, Libya

Post by behind_you »

This function reads and returns the specified line of text from the specified file. Note that the file you want to read must be located inside a folder (in your gamefolder) called 'data' (without quotes).

Here's the code:

Code: Select all

string (string filename, float line) readtextline =
{
	local float file_read, readcount;

	file_read = fopen(filename, FILE_READ);//Open the file in read mode

	while(readcount < line)//loop until condition met
		{
		filename = fgets(file_read);//everytime you use fgets, it gets a line of the file. if you use it repeatedly, you will get every next line of text. Do it the specific amount of times and you will get the line of text on the line you wanted
		readcount = readcount + 1;//simple counter that ends the loop when needed
		}

	fclose(file_read);//closes the file (important)

	return filename;//return the line of text!
};
The code is heavily commented (as usual) for your learning.

To use this, assign a string to this value:

readtextline (FILENAME, LINE);

Replace FILENAME with the name of the file (must be inside 'data' folder).
Replace LINE with the line of text you want to return (AKA '3' will return the 3rd line of text in the file).

Enjoy!
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Post by toneddu2000 »

Could this last function be used to read some ingame text? Example: you're walinkg in a room and you find a book, as you collect it a message appears on the screen and displays the content of the book(read by a text file in the data folder).
It would be awesome! Thanks behind_you for this very interesting new section!
Nahuel
Posts: 495
Joined: Wed Jan 12, 2011 8:42 pm
Location: mar del plata

Post by Nahuel »

toneddu2000 wrote:Could this last function be used to read some ingame text? Example: you're walinkg in a room and you find a book, as you collect it a message appears on the screen and displays the content of the book(read by a text file in the data folder).
It would be awesome! Thanks behind_you for this very interesting new section!
i believe that you have a good idea for the use of the FRIK_FILE!
For example, I was using texts already declared in qc or in the map, but using the frik file it is possible to "personalize" the text according to what the player does. It is an excellent idea!
hi, I am nahuel, I love quake and qc.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Post by toneddu2000 »

Hi Nahuel, can you post some link to the documentation of FRIK_FILE?
Is it the same of this?
Nahuel
Posts: 495
Joined: Wed Jan 12, 2011 8:42 pm
Location: mar del plata

Post by Nahuel »

toneddu2000 wrote:Hi Nahuel, can you post some link to the documentation of FRIK_FILE?
Is it the same of this?
yes! we are speaking about it, (FILE_READ ...) :)
hi, I am nahuel, I love quake and qc.
toneddu2000
Posts: 1395
Joined: Tue Feb 24, 2009 4:39 pm
Location: Italy

Post by toneddu2000 »

ok,thanks!
behind_you
Posts: 237
Joined: Sat Feb 05, 2011 6:57 am
Location: Tripoli, Libya

Post by behind_you »

Today I will show you how to centerprint one letter at a time instead of the entire thing at once. I thought about this while playing Half-Life. The text that prints in HL prints in this kind of style.

This is the code:

Code: Select all

void() charprint =
{
	local float howmany, blankcount;
	local string text, blanks;

	howmany = strlen(toprint);//count how many letters to print
	
	if (howmany >= textpos)//if your printing position is smaller than or equal to the amount of letters in your text
		{
		textpos = textpos + 1;//add one to your textposition
		text = substring (toprint, 0, textpos);//this substrings your text by the value of textpos. So if textpos equaled 2 and the text to print was "PRINTING", you would print "PR". If it were 3, you would print "PRI" and so on. Since textpos goes up every time by 1, you would get a letter by letter printing effect
		while(blankcount < (howmany - textpos))//while loop: whenever the blankcount is smaller than the difference between the character count and the typing text position
			{
			blanks = strcat(blanks, " ");//add a blank to the blanks string
			blankcount = blankcount + 1;//add to the blankcount(prevents runaway loop and tempstring errors)
			}
		text = strcat(text, blanks);//combine the text and the blanks. The point of this is to get rid of the constant centering the engine does with centerprints. Without the constant centering, it looks better IMO. If you want the constant centering, then remove this strcat line and the while loop.
		//Optional: Add a sound code here. This sound will be played every time an extra character is printed (A typewriter or keystroke sound is best)
		}
	else//otherwise
		{
		text = toprint;//print the whole text
		return;//End the loop which would cause the text to disappear in a few seconds
		}

	centerprint(self, text);//print the current text
	
	self.nextthink = time + 0.02;//loop in 0.02 seconds. Higher value = slower print
};

void(string whattowrite) initcharprint =
{
	toprint = whattowrite;
	textpos = 0;
	//Resets all the globals once for use again
	self.think = charprint;
	self.nextthink = time + 0.1;//start printing!
};
Make sure you add this in defs.qc

Code: Select all

float textpos;
string toprint;
There you have it! To use this, stick this line of code somewhere where it will be triggered (MAKE SURE IT IS ONLY TRIGGERED ONCE PER USE):

initcharprint(THETEXT);

Obviously, replace 'THETEXT' with the string you want to print (ie "I would like to print the following").

Enjoy!

EDIT: toneddu2000 to do what you want, create a .string called booktext and a float called .nextpage, then every time you create a book in the map assign it's self.booktext to a file inside the 'data' folder which will contain the text to read. Then whenever you go and read the book, do this line:

readtextline (self.booktext, self.nextpage);

Then make it so that whenever you click a button, increase the book's nextpage by one, to read the next line of text. Centerprint each text! That's a simple way of doing it.
Post Reply