Index | FAQ | Search | Memberlist | Usergroups | Profile | Log in to check your private messages | Log in | Register

Adding new weapons and binary operations... Goto page 1, 2 Next

| Inside3d Forums Forum Index -> QuakeC Programming

View previous topic :: View next topic
Author | Message
--- | ---
GaciX 69 Joined: 27 Feb 2010 Posts: 11 Location: Chile |

Posted: Thu Mar 18, 2010 8:34 pm Post subject: Adding new weapons and binary operations... |

Hi everybody! Well, me and a couple of friends decided to make a mod for Quake. Because I was the only one who knew how to program, I was chosen as main programmer for the project. The problem is that I never worked before with QuakeC, and all my previous experience was using Decorate in Doom and making little programs in Java. Luckly, I discovered this website, and I learned a lot from the tutorials that are in here.
Going now to my problem, I started adding some new weapons to my mod, using this tutorial as a base: http://www.inside3d.com/showtutorial.php?id=60
My question comes when I'm trying to add a new weapon, so we have the following code in defs.qc:

Code:
// items float IT_AXE = 4096; float IT_SHOTGUN = 1; float IT_SUPER_SHOTGUN = 2; float IT_NAILGUN = 4;
float IT_SUPER_NAILGUN = 8;
float IT_GRENADE_LAUNCHER = 16;
float IT_ROCKET_LAUNCHER = 32;
float IT_LIGHTNING = 64;
float IT_EXTRA_WEAPON = 128;
float IT_UZI = 256; //My new weapon

The problem comes when I add a new weapon, because all the references of the other weapons are expressed as power-of-2 numbers. So my question is if it's really necesary to follow that rule or if I can add odd numbers as a reference, I ask all of this because further down in my code, I make various "weapon checks" to see if the player has a certain weapon.
Here is an example of the code that I did to replace the W_ChangeWeapon() function in weapons.qc, using my "weapon check":

Code:
void() W_ChangeWeapon = { local float it, am, fl; it = self.items; am = 0;
if (self.impulse == 1)
{
fl = IT_FISTS;
}
//Code to assign 3 weapons to a same impulse command
//Just press the 2 button and you will be cycling through the Uzi, the Shotgun, and the Super Shotgun
else if (self.impulse == 2)
{
if (self.weapon != IT_UZI && self.weapon != IT_SHOTGUN && self.weapon != IT_SUPER_SHOTGUN)
{
if (it & IT_UZI && self.ammo_shells >= 1) //See? I use "it & IT_UZI" to see if the player has the Uzi
fl = IT_UZI;
else if (it & IT_SHOTGUN && self.ammo_shells >= 1)
fl = IT_SHOTGUN;
else if (it & IT_SUPER_SHOTGUN && self.ammo_shells >= 1)
fl = IT_SUPER_SHOTGUN;
}
else if (self.weapon == IT_UZI)
{
if (it & IT_SHOTGUN && self.ammo_shells >= 1)
fl = IT_SHOTGUN;
else if (it & IT_SUPER_SHOTGUN && self.ammo_shells >= 1)
fl = IT_SUPER_SHOTGUN;
else if (it & IT_UZI && self.ammo_shells >= 1)
fl = IT_UZI;
}
else if (self.weapon == IT_SHOTGUN)
{
if (it & IT_SUPER_SHOTGUN && self.ammo_shells >= 1)
fl = IT_SUPER_SHOTGUN;
else if (it & IT_UZI && self.ammo_shells >= 1)
fl = IT_UZI;
else if (it & IT_SHOTGUN && self.ammo_shells >= 1)
fl = IT_SHOTGUN;
}
else if (self.weapon == IT_SUPER_SHOTGUN)
{
if (it & IT_UZI && self.ammo_shells >= 1)
fl = IT_UZI;
else if (it & IT_SHOTGUN && self.ammo_shells >= 1)
fl = IT_SHOTGUN;
else if (it & IT_SUPER_SHOTGUN && self.ammo_shells >= 1)
fl = IT_SUPER_SHOTGUN;
}
}
//Here continues the rest of the code

As you can see, I'm using a binary operator to check the weapons: (it & IT_UZI checks if the player has the Uzi, where it = self.items). The problem here is that I just copied this code from another part of the code because I didn't find another way to make a "weapon check" (since QuakeC doesn't have booleans, unlike Java and other languages). The problem arises here too because if I assign an odd number to IT_UZI, it has a very rare behaviour when I compile and test, and also my mod is going to have a lot of new items and weapons. So if the value assigned to every object in defs.qc is a float number and a power-of-2, the maximum number that i can place is 2^127 (from what I know that's the maximum number that a float can have), meaning that i have space just for 128 objects (from 2^0 to 2^127).
So, making this problem a little bit shorter, my questions are:
1) Can I assign an odd number as a reference for a weapon or an object in defs.qc? In that way I have more space for new items and I don't collapse the engine with big numbers as 2^127.
2) So, if it's possible to use odd numbers as a reference for an item, but it & IT_UZI does weird stuff when IT_UZI is an odd number. Is there another way to check if a the player has a certain weapon/object without using a binary operator?
3) Since I have assign in my code to "impulse 2" the cycling of several weapons. Is there a way to make my code more efficient or shorter?
Thanks in advance to anybody who can answer anyone of my questions...
Greetings!
PS: Excuse me for my bad english, and also for my lack of QuakeC experience. This is my first time doing a mod for Quake...

Back to top |

Sajt Joined: 16 Oct 2004 Posts: 1026 |

Posted: Thu Mar 18, 2010 9:16 pm Post subject: |

No, you can't use an "odd" number. Due to the nature of bitwise math, the number 11 for example is actually 1 + 2 + 8, so it refers to three flags. If you need to know more about bitwise math, I'm sure there are plenty of resources out there, but to summarize, yes, a bit flag must be a power of two.
The highest bitflag you can store in a float is actually, if I remember correctly, 2^23 (8388608). Therefore, the maximum value of all bits being used is 2^24-1 (16777215). Above that, there is not enough precision in a 32-bit float to maintain the integer portion of the number.
You can't use 256 because you're encroaching on other bitflags which are also stored in .items. You'll see in defs.qc that IT_SHELLS uses 256.
If you want to store more items, you should create a second field by adding this to the bottom defs.qc:
.float items2;
And then store your new items in that field rather than .items. There is probably a lot of annoying work involved in getting this all working, and after all that you will still have to deal with the .weapon field. It's not a lot of fun especially if you aren't familiar with how it works. I can only suggest that you take it slowly, make sure you know exactly what something does before you change it, and if something unexpected ever happens when you test your changes, figure out exactly why that is happening before you move on. And of course come back here whenever you have questions.
The tutorial you linked to seems to be very evil. "Since we don't want any bit fields", the author says... well, maybe he doesn't want any bit fields. Anyway, I really doubt that that tutorial will actually work. For one thing, the bottom of W_ChangeWeapon() does do a bitwise AND to check whether the weapon 'fl' is contained in self.items. That won't work as expected when the item flag is not a power of two. (For example, if IT_SNIPER is 17, that is 1 + 16, so you will only be able to switch to the weapon when you are holding either the shotgun or the grenade launcher. Only because you are always holding the shotgun, that will work, but I doubt even the author is aware of his ignorance.)
_________________
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.

Back to top |

Spike Joined: 05 Nov 2004 Posts: 944 Location: UK |

Posted: Thu Mar 18, 2010 10:23 pm Post subject: |

My personal method is to make a separate .weapons field and use that for weapons instead of .items. And then mirror the extra bits into .items/.items2 when .weapons changes so to keep the sbar sane. Or you could use csqc and ditch .items as far as weapons is concerned.
Note that IT_EXTRA_WEAPON isn't actually used, you can rename and use as desired.
Otherwise what Sajt said.
_________________
What's a signature?

Back to top |

GaciX 69 Joined: 27 Feb 2010 Posts: 11 Location: Chile |

Posted: Thu Mar 18, 2010 11:30 pm Post subject: |

Thanks to both of you guys, this thing of using an "odd" number was really confusing me. Now I'll try by making a separate field for weapons and items. I'll try to inform you if a question or problem arises in the way...

Spike wrote:
Or you could use csqc and ditch .items as far as weapons is concerned.

Excuse my ingorance about this, but what is csqc? I have read a few other threads and in some of them appears the same answer about using csqc as an alternative. Although I'm using Clean Quake 1.6 source as a base for my mod (to keep it simple), I'm intrigued about csqc. Could anybody please link me a thread about it or explain me in short what is csqc?

Back to top |

Spike Joined: 05 Nov 2004 Posts: 944 Location: UK |

Posted: Thu Mar 18, 2010 11:45 pm Post subject: |

overcomplicated for most things, that's what it is. :P _________________ What's a signature?

Back to top |

Lardarse Joined: 05 Nov 2005 Posts: 243 Location: Bristol, UK |

Posted: Thu Mar 18, 2010 11:47 pm Post subject: |

CSQC is a long-term joke of the Quake community that stopped being funny a long time ago and is now just getting old... _________________ <ekiM> Son, you're writing data structures your CPU can't cache.

Back to top |

Spike Joined: 05 Nov 2004 Posts: 944 Location: UK |

Posted: Thu Mar 18, 2010 11:59 pm Post subject: |

CSQC = Client Side QuakeC. Its really quite powerful. Being clientside it can draw your hud in maaany different ways, however you want using whatever fields you want, freeing you up so you don't have to use .items for everything, it could allow you to make a separate field for each individual weapon you have, or create an inventory system with a click+drag gui...
Basically a simple set up allows you to read fields from the player's entity on the server, and display your own hud.
However, that's overkill for your needs. Truely it is. Sorry for mentioning it, and it only really works in two engines (perhaps 3, but that extra one doesn't count), and its not even compatible between those.
Back on topic...
The .items field contains a bitmask of the various items the player has and should be shown on the hud. It contains armour type, ammo type, powerups, and weapons. Its a float, so you only get 23 bits before your numbers start to become aproximated and strange things will then happen.
Anyway, the server has no interpretation on the matter, it sends those 23 bits to the client without really caring. The client does, interpret them, however, and will display the various hud items based on which bits are currently set.
Binary maths 1+2+4+8+16 etc all add up. Because it doubles each time, you know that the only way you could have the number 7 in there is if 1+2+4 were all added together.
You don't have to use .items to hold the bitfield of the weapons you currently own. But do note that if you don't maintain .items, your hud will look somewhat spartan...
The .weapon field contains the bit that represents the currently active weapon.
Note that the axe, at 4096, is never drawn on the hud. You can theoretically make the axe always owned, and use that bit for marking ownership of something else. Megahealth is never drawn either. And frankly, does it matter if you have megahealth or not, when its a seperate entity taking your health away like it does. By reworking that code as nessesary, it should be possible to free up that bit too. IT_EXTRA_WEAPON is a placeholder and nothing else. Nothing uses it. There's a 3rd bit (its the easiest).
Now, .items2... Remember I said that the server sends off the 23 bits of the items field to the client? Well actually it sends a 32bit integer rather than a 23bit one. The upper 11 bits are filled in with the lower bits pulled from the items2 field (if its not present, it'll grab the serverflags global instead). This is interesting because this is how runes normally work. By also using items2 for storing ownership of extra weapons, you can use the rune images on the hud for the extra weapons.
How you do all of this is entirely up to you.
_________________
What's a signature?
Last edited by Spike on Fri Mar 19, 2010 12:14 am; edited 1 time in total

Back to top |

ceriux Joined: 06 Sep 2008 Posts: 969 Location: Florida, USA |

Posted: Fri Mar 19, 2010 12:09 am Post subject: |

couldnt you just write a functin which uses the IT_WEAPONNAME instead? " if (self.weapon == IT_UZI) " ? _________________ QuakeDB - Quake ModDB Group

Back to top |

Wazat Joined: 15 Oct 2004 Posts: 732 Location: Middle 'o the desert, USA |

Posted: Sun Mar 21, 2010 1:41 am Post subject: |

If you need more bits than the 24 provided, you have several options: Many "massive weapon mods" that have tons of weapons have a clever system for storing & selecting them. From the user's perspective, if he wants to select the sticky bomb he just taps the 6 key several times until it's selected. As long as you don't have more than 3 or 4 weapons per number key, this is an effective way to catalog multiple weapons.
Under the hood, this works by having several .floats to store the various "extra" weapons' presence flags. You could have 1 .float per number key (for example, additional melee weapons are stored in .items1, explosives stored in .items6 and 7), or if the weapons are few enough you could just 1 or 2 extra fields. The problem here is that you must always know which field to check for each weapon, which is a astronomical pain in the arse when your weapon set gets huge.
Another option is when the player picks up a weapon, he gets all weapons in that number key. Picking up the grenade launcher gives him the sticky bomb, cluster grenades, etc too. He gets lots of free weapons this way though, which you may not want.
Still another option is "weapon sets". When you pick up the Assassin Weapon Set, you get 1 weapon for each weapon number (#1 is assassin's dagger, #2 is crossbow, etc). Then when you pick up the Demolitions Weapon Set, you get 1 additional weapon in each #, and hitting a number key repeatedly will cycle through the various sets. This may be complex to set up though (especially for someone new to QC), and it may also not fit with the game theme you're pursuing.
There is a way to create an extremely open-ended (and mostly automated) system that allows for any number of weapons. With this system we will be able to assign sequential ids to weapons (11, 12, 13, etc), instead of using powers of 2. I recommend you still allow selecting them by pressing number keys repeatedly or something even more helpful (it's not fun having to hit the 7 key 8 times to select a weapon, but it's even worse to have a hundred impulses or console commands to remember).
The way this system works is, .weapon and .items become entirely HUD-based. They are no longer checked by the code to see what weapons the player possesses or has selected. They're just set to update the player's hud (items like powerups etc are still okay; we're just concerned with weapons here). We then create a .selWeapon field that stores the player's selected weapon, and a .itemsArray field that uses fteqcc's array support. Fteqcc is an excellent compiler, by the way. I recommend it for your projects (frikqcc is very good too, though I suspect its array support is still broken/wonky).
Here's how it happens: We write a a series of helper functions that calculate which index we use in the .itemsArray field, and what bitflag to use for the bitwise & operation. Once the helper functions are done, we rarely use bitwise anymore -- .selWeapon uses the sequential weapon ids to indicate which weapon we have selected, and we use those same ids when calling our helper functions.
Here's what we need to know to help us write this feature:
1) Each .float can only hold 24 bitflags. So the array size of .itemsArray needs to be the weapon count in your mod / 24 (rounded up).
2) To check if a player currently possesses a weapon, we need to divide the weapon id by 24 and round up. That's the array index to check.
Then, to determine which bitflag is needed, we get the remainder (the modulus, which we have a function to calculate somewhere in the forums, or possibly in the frikbot source code -- mathlib.qc?). Then use the remainder as the power of 2:

Code:
index = ceil(wepid / 24); // wepid is an argument to the function if(index < 0 || index >= WEP_INDEX_ARR_COUNT) { // sanity check: prevent a crash if someone passes in an invalid weapon id, and print a warning to annoy developer(s) into fixing it.
bprint("warning: invalid id passed to hasWeapon(): ");
bprint(ftos(wepid));
bprint("\n");
return FALSE;
}
power = modulus(wepid, 24); // find in the mathlib code, which someone here will know how to find. May actually be named "mod"
bitflag = pow(2, power); // again, mathlib
return pl.itemsArray[index] & bitflag; // pl is an argument into the function

Ta-da! This is pseudo code for a function (untested and my qc skills are rusty), but barring any mistakes this should be a great first step.
You also will want to write a similar function for adding weapons to the player's inventory. It follows a similar pattern, and instead of returning the itemsArray[index] & bitflag, it does this:

Code:
pl.itemsArray[index] = pl.itemsArray[index] | bitflag;

Resetting the player's weapons is as simple as looping through the array and setting it to 0, then calling the add weapon function to add his starting weapons back in.
Selecting weapons is done with another similar function. The difference is, we want to set the player's .weapon field as well as his .selWeapon field. Remember, we do this to make his HUD look good. How do you decide which weapons light up which numbers? Meh, your choice. You could catalog them by type (explosives go on #6, spread go on #3, etc), by power level (weapons on #3 are weaker than those on #7), etc. Your choice. The main reason to keep the hud functioning is because we are letting the player press a number key multiple times to select a weapon. Highlighting the number key makes it easier for the player to learn which weapon # maps to each player.
The big trick is changing maps. There are only 16 floats (parms) that can be used for storing data across maps for each player, and 9 are already used for health etc. This means you have 7 parms, for a total of 7*24=168 weapons. If you need to store more, you may need to get creative and scrunch extra data into some of the parms to free up another slot (it may be possible to combine armorvalue and armor type into the same field with some math trickery). Alternately, if you want to dedicate yourself to the engines that support FRIK_FILE, you can use it to have full file IO at your disposal. This is what Conquest does, because I needed lots of data stored on each of the player's weapons and shields, and the params weren't possibly enough.
If your mod doesn't want weapons etc transferring across maps (i.e. it's a deathmatch game and players must always start fresh upon map start), then this is not a problem at all.
Anyway, I've probably chatted your ears off and reduced you to gnawing your arms off like a trapped coyote. My posts tend to run long, my apologies. Anyway, this ought to give you some ideas and give you enough information to get started. Cheers!
-Wazat
_________________
When my computer inevitably explodes and kills me, my cat inherits everything I own. He may be the only one capable of continuing my work.

Back to top |

Downsider Joined: 16 Sep 2008 Posts: 478 |

Posted: Sun Mar 21, 2010 2:01 pm Post subject: |

Quake-C supports arrays? How about multi-dimensional arrays?

Back to top |

Wazat Joined: 15 Oct 2004 Posts: 732 Location: Middle 'o the desert, USA |

Posted: Sun Mar 21, 2010 6:41 pm Post subject: |

Downsider wrote:
Quake-C supports arrays? How about multi-dimensional arrays?

I think so. You have to use an array-friendly compiler to do it at all (i.e. fteqcc), but I assume multidimensional is fine too.
_________________
When my computer inevitably explodes and kills me, my cat inherits everything I own. He may be the only one capable of continuing my work.

Back to top |

frag.machine Joined: 25 Nov 2006 Posts: 728 |

Posted: Sun Mar 21, 2010 7:46 pm Post subject: |

Be aware that fteqcc array support will require FTE/QW (or maybe DarkPlaces, not sure if the resulting progs.dat would work without changes on both). It's a good alternative if you're not concerned about your mod being restrict to one or two engines, actually.
Other compilers (frikqcc ?) fake arrays behavior adding contiguous fields to the entity structure (ex: .float element0, .float element1, .float element2... for a float array called "element").
_________________
frag.machine - Q2K4 Project
http://fragmachine.quakedev.com/

Back to top |

Spike Joined: 05 Nov 2004 Posts: 944 Location: UK |

Posted: Sun Mar 21, 2010 10:18 pm Post subject: |

fteqcc's arrays work with any engine, they require no extensions. That said, some older versions of DP can complain, you can work around that by using -Fno-fastarrays when compiling and it'll work in any engine, even on the dreamcast. They're a little quirky when used with fields, from what I remember. They need extra brackets or something.
_________________
What's a signature?

Back to top |

GaciX 69 Joined: 27 Feb 2010 Posts: 11 Location: Chile |

Posted: Mon Mar 22, 2010 7:46 pm Post subject: |

Thanks Wazat, I really like your approrach to my problem. In fact, I was thinking from the begining in using arrays for the problem of assigning multiple weapons, but I didn't know if QuakeC could handle arrays. Any way, I understand some parts of your code, but I have a couple of questions about it:

Wazat wrote:

Code:
index = ceil(wepid / 24); // wepid is an argument to the function if(index < 0 || index >= WEP_INDEX_ARR_COUNT) { // sanity check: prevent a crash if someone passes in an invalid weapon id, and print a warning to annoy developer(s) into fixing it.
bprint("warning: invalid id passed to hasWeapon(): ");
bprint(ftos(wepid));
bprint("\n");
return FALSE;
}
power = modulus(wepid, 24); // find in the mathlib code, which someone here will know how to find. May actually be named "mod"
bitflag = pow(2, power); // again, mathlib
return pl.itemsArray[index] & bitflag; // pl is an argument into the function

1.- Where do i put this code? In the end of defs.qc? Or in W_ChangeWeapon() in weapons.qc? Or in a separate .qc file?
2.- From what I understand, 'wepid' stands for the number id assigned to each weapon in defs.qc (that now doesn't need to be power of 2). Is this true?
3.- What does 'WEP_INDEX_ARR_COUNT' stands for? Or it's just a default variable for the size of the array?
As I said before, excuse me if my questions are a little bit stupid. This thing of using arrays and bitwise operations in QuakeC is new for me...

Back to top |

Wazat Joined: 15 Oct 2004 Posts: 732 Location: Middle 'o the desert, USA |

Posted: Tue Mar 23, 2010 2:44 am Post subject: |

Hey there. Sorry about that, I wrote a lot of pseudo code and made up variables as I went along. Let me explain. 1) Make a new function and put the code inside. Weapons.qc is a good place to put it (near the other weapon change code for convenience). You might name it hasWeapon:
/*
hasWeapon
Returns TRUE if the player pl currently has the weapon defined by wepid.
*/
float hasWeapon(entity pl, float wepid)
{
//...
}
Note that with new compilers you no longer need to do the QC-only syntax like = after the function name, or ; after the function's }.
2) Correct. wepid is the non-power of 2 weapon identifier that you'll pass into the function when calling it.
3) Ah, yes. That was a vague reference that requires a few mental jumps you didn't have the background to make. Bad Wazat, no snack.
Here are a couple ways to do this: When you declare your array, define a float that indicates the array's size. Whenever you add enough weapons to increase the array size, bump up WEP_INDEX_ARR_COUNT too.
Alternately (depending on what's more maintainable feeling to you), you could define a float that's the last weapon id, and use it to auto-calculate this. For example, in your weapon defines:
float WEP_AXE = 1;
float WEP_SHOTGUN = 2;
float WEP_SUPERSHOTGUN = 3;
//...
float WEP_FLAMETHROWER = 32;
float WEP_RAILGUN = 33;
// NOTE: Always update when adding a new weapon
float WEP_LAST_WEAPON = 33;
Then replace WEP_INDEX_ARR_COUNT in the function below with ceil(WEP_LAST_WEAPON / 24).
I'd also recommend defining this:
float WEAPONS_PER_FLOAT = 24;
Then replace 24 in our math with this constant. The reason I strongly recommend this is because magic numbers are confusing and make the code hard to maintain. It's hard coming back to some code months or years later and thinking "what does the 24 mean? Why do I divide by that, again?".
I hope that helps!
-Wazat
_________________
When my computer inevitably explodes and kills me, my cat inherits everything I own. He may be the only one capable of continuing my work.

Back to top |

Display posts from previous:

| Inside3d Forums Forum Index -> QuakeC Programming | All times are GMT Goto page 1, 2 Next
Page 1 of 2

Jump to:

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

Powered by phpBB © 2004 phpBB Group