Announcement

Collapse
No announcement yet.

Interactive QuakeC tutorial

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • Interactive QuakeC tutorial

    I thought I would start this thread to give people ideas how to go about hacking QuakeC code to do things and see why Quake can still be so much fun to play. Long after you beat the game you eventually want to change something, maybe just make it harder. Well there used to be lots of tutorials on QuakeC but they aren't many around on the internet anymore. So this thread will be kind of an interactive tutorial where I can show you some little things you can do and how you can go about making quake do whatever you want.

    Now you need to know how to program to do QuakeC. But I'm going to just jump right in and show you to do stuff and get immediate feedback from the game, and you will learn all this code. Trust me it is not hard at all, it just takes some desire to want to learn it.

    Ok so I thought what would be a good little snippet to get started with? Well did you ever notice in Quake, when you get hit by a grenade, or you shoot a monster with a rocket, it doesn't go flying? Doesn't it seem strange that explosions don't send monsters flying through the world? Well you are in luck because it is pretty easy to do this.

    Now first you have to get the quakec code and the compiler. I am pretty sure you can find this stuff on this site. But if you can't, go to ftp://ftp.idsoftware.com. Go into the source directory. Somewhere in there should be something like qccsource. If you have trouble finding it then I will upload the code somewhere for you. Now you need the quakec compiler. Well you are in luck there, because one is available on this site. It's called qccx and it's in the downloads. This is much better than the original compiler anyways, as it will catch more errors that you might make. It even caught some errors that were made in the original source code.

    Ok so now hopefully you are set up and ready to roll. Now you need one more thing. Some way to edit the files. Well, you can use notepad which comes with windows. I like QCide myself, but notepad will work fine. Now find where you extracted the quakec files and look for a file called combat.qc. That is all the quakec code that is used for combat.

    In combat.qc you will scroll down and find a function called T_Damage that looks like this

    Code:
    void(entity targ, entity inflictor, entity attacker, float damage) T_Damage=
    Scroll down until you see this:

    Code:
    // figure momentum add
    	if ( (inflictor != world) && (targ.movetype == MOVETYPE_WALK) )
    	{
    		dir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5;
    		dir = normalize(dir);
    		targ.velocity = targ.velocity + dir*damage*8;
    	}
    now select that last line and paste this over it

    Code:
           self = targ;
    	if ( (inflictor != world) && (targ.movetype == MOVETYPE_WALK) )
    	{
    		dir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5;
    		dir = normalize(dir);
    		
                    self.velocity = self.velocity + dir*damage*20;
                    self.avelocity = dir*damage*50;
    		
    		// lift the monster off the ground so it flies
    		if (self.flags & FL_ONGROUND)
    			self.flags = self.flags - FL_ONGROUND;
            }
    Now compile the code with qccx and if it all goes correctly, there will be a progs.dat file. That is the compiled QuakeC code. If something went wrong, qccx will tell you a line number with an error. Hopefully if you did this right though, you won't have any errors. Now you need to put this progs.dat file somewhere where quake will find it. The best thing is to create your own mod folder in the Quake directory and put in there. Then you start the game with quake +game directory where the directory is the name you used. You can stick it in the ID1 folder, but keep in mind that some pak files have their own progs.dat, and yours might not get loaded if it finds another one first.

    So now what does it do? Well you see T_Damage is called whenever something is hurt. When you get hit by a lightning bolt, T_Damage is called to inflict damage on you. You see the self.velocity? Well that is just some variable that tells quake how fast you are moving. Self is an entity which has information about the monsters and things in game. Self is a special entity in the engine, but don't worry about it right now. Whenever you see self, just know that it is the special entity that quake is currently interest in. Just understand this, we are calculating the effect of the damage and adding momentum to the object. That is a force, pushing you back in the direction of whatever hit you. Don't worry if this is confusing right now, it will begin to make sense and you will see it is really very easy to make Quake do almost anything you want to in the game with QuakeC. That is why QuakeC is so much fun, it is very easy to do so much in it. But there is alot you can't do, but you will learn that later.

    Ok the next thing is self.avelocity. This is applied velocity, like self.velocity, but this is a force being applied to the entity. Quake is pretty old, and it's physics are pretty rudimentary. You just have a velocity, the speed you are moving at, and the avelocity, which is a force to act on the entity. That's all you need to know to make stuff move in Quake.


    The last bit says, if the self.flags has the flag FL_ONGROUND on it, then let's unset this. This makes it so the entity is not on the ground, so it will go flying away.


    Well I hope people can find this useful, and if you have problems feel free to ask me, I have plenty of free time right now unfortunatley, or fortunately for you, if you want to learn I will try to help but I don't know everything and I programmed in quakec for years in the past. But there is always more to learn, new engines people hack with new stuff in it, and so much stuff to do so you will never grow bored.
    Last edited by Quake Jesus; 07-14-2011, 11:53 PM.

  • #2
    Wow, I was bloody cringing while reading this.

    I can understand being new, but did you even test this code? First of all it doesn't do what you say it does, second it completely breaks the game.

    You also have the following wrong:
    • Self is not some special entity that Quake is currently interested in, self is set to point at different entities during specific events in the engine, but it is not necessary to set self to point to the entity you are currently working with.
    • .avelocity is NOT acceleration, it is ANGULAR VELOCITY.


    Here's the code that actually does what you want:

    Code:
    if (inflictor != world && targ.movetype == MOVETYPE_STEP)
    	{
    		       
    			if (targ.classname == "monster_army")
    				power = 8;
    			else if (targ.classname == "monster_demon1")
    				power = 5;
    			else if (targ.classname == "monster_dog")
    				power = 9; 		       	
    			else if (targ.classname == "monster_enforcer")
    				power = 7;
    			else if (targ.classname == "monster_fish")
    				power = 0; // water
    			else if (targ.classname == "monster_hell_knight")
    				power = 5;
    			else if (targ.classname == "monster_knight")
    				power = 7;
    			else if (targ.classname == "monster_ogre")
    				power = 4;
    			else if (targ.classname == "monster_oldone" || targ.classname == "monster_boss")
    				power = 0; // bosses can't be pushed
    			else if (targ.classname == "monster_shalrath")
    				power = 4;
    			else if (targ.classname == "monster_shambler" || targ.classname == "monster_armagon")
    				power = 1;
    			else if (targ.classname == "monster_tarbaby")
    				power = 0; // sticky
    			else if (targ.classname == "monster_wizard")
    				power = 0; // flies
    			else if (targ.classname == "monster_zombie")
    				power = 9;
    		
    			if (power)
    			{
    				dir = targ.origin - inflictor.origin;
    				dir = normalize (dir);
    				targ.flags = targ.flags - (targ.flags & FL_ONGROUND);
    				targ.velocity = dir * damage * 8;
    				targ.velocity_z = targ.velocity_z + 70;
    			}
    	}
    Paste that below

    Code:
    	if ( (inflictor != world) && (targ.movetype == MOVETYPE_WALK) )
    	{
    		dir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5;
    		dir = normalize(dir);
    		targ.velocity = targ.velocity + dir*damage*8;
    	}
    not over it.

    Also add local float power; at the top.

    Comment


    • #3
      Thanks Guys,

      I especially like the "cut and paste + a run through of what the code sections do" approach as it aides understanding tremendously (for me at least).
      Personally, even though there is the inside 3d site, I've been quietly hoping for a helpful "how to" thread like this for some time as I believe seeing how Quake can be improved explained in simple, friendly language creates sparks of interest and makes some people think "I could do that".
      So, much kudos to you both for trying to be helpful and IMHO providing one of the most interesting thread ideas for quite a long time. I can only hope there will be further examples such as how to add additional weapons (not just replace existing ones), voting, how to to pull items towards you using a hook, and so on.

      Incidentally, I had always toyed with the idea of applying this "blast intertia" to other entities in the map to add a little more realism. For example, if a rocket lands near a health pack or ammo box wouldn't that health pack be blown out of position, even a little? With this in mind could this technique be applied for that purpose? If so, how? Presumably add targ.classnames for the various entities (healthpacks, ammo, etc) but I'm guessing I would need to alter targ.movetype and a few other bits and pieces?

      Any constructive suggestions to an interested QC newb such as myself would be much appreciated

      Kind regards

      Monty
      Last edited by Mr.Burns; 07-15-2011, 03:15 PM.
      Mr.Burns
      "Helping to keep this community friendly, helpful, and clean of spammers since 2006"
      WWW: Quake Terminus , QuakeVoid You Tube: QuakeVoid
      Servers: Quake.shmack.net, damage.servequake.com

      News: JCR's excellent ctsj_jcr map is being ported to OOT

      Comment


      • #4
        Interesting.

        Comment


        • #5
          How about a vid of this in action please? It sounds a lot like the gyro feature
          Last edited by gdiddy62; 02-26-2012, 11:40 AM.

          Comment


          • #6
            that power code makes no sense. All these powers are being set but nowhere is power being used in the final math. Instead of
            Code:
            	targ.velocity = dir * damage * 8;
            I think it's maybe supposed to be -
            Code:
                targ.velocity = dir * damage * power;
            Of course I could be wrong since I don't know what the hell I am talking about, but what I do know is, setting all those powers is useless if "power" isn't used for more than checking if it exists. (if(power)). then again, checking for power is also useless, unless you intend to use monsters that aren't listed in that string of if statements. FricQC (-sp) has switch/case support. So you could handle the monsters like the below. This allows you to let default catch all the monsters that have a power of 0 as well as assign any added monster a power of 0, if you forget to add them to the switch/case, making (if(power)) useless code.


            Code:
            switch(targ.classname)
            {
                case "monster_army":
                    power = 8;
                    break;
                case "monster_demon1":
                    power = 5;
                    break;
                case "monster_dog":
                    power = 9;
                    break;
                case "monster_enforcer":
                    power = 7;
                    break;
                case "monster_knight":
                    power = 5;
                    break;
                case "monster_hell_knight":
                    power = 7;
                    break;
                case "monster_ogre":
                    power = 4;
                    break;
                case "monster_shalrath":
                    power = 4;
                    break;
                case "monster_shambler":
                    power = 1;
                    break;
                case "monster_armagon":
                    power = 1;
                    break;
                case "monster_zombie":
                    power = 9;
                    break;
                defaut:
                    power = 0;
                    break;
            }
            http://www.nextgenquake.com

            Comment


            • #7
              I think RG was just in a hurry when he posted that, * power was just a simple oversight. I didnt know Frikqcc had switch support, but iv'e been stuck with v2.5 for the longest. FTEQcc seems the best TBH
              if your default is setting power to 0 u still need if (power) before the velocity change portion. Or else multiplying the velocity by 0 well make the monster immobile...
              www.quakeone.com/qrack | www.quakeone.com/cax| http://en.twitch.tv/sputnikutah

              Comment


              • #8
                That's what I meant (my mistake) FTEQCC (it has array support too)...there are so many of these damn things and I honestly don't know what the hell I am talking about for the most part (lol). I have been programming for about 15 years and my understanding of QuakeC pretty much encompass it's relation to being a programming language (ex - functions, if statements, declaring var types etc). When you get into what actually makes it QC, my education pretty much stops.

                if your default is setting power to 0 u still need if (power) before the velocity change portion. Or else multiplying the velocity by 0 well make the monster immobile...
                Oh wow, so in QuakeC if something equals 0 it will return false in an if statement? Well, why bother to set anything to zero then? Just don't set anything and !power will be automatic, or set power to zero when you declare it at the top.

                Code:
                local float power = 0;
                switch(targ.classname)
                {
                  //cases for all monsters with a power over 0
                }
                if(power)
                {
                  //code
                }
                right?
                Last edited by MadGypsy; 02-28-2012, 09:00 PM.
                http://www.nextgenquake.com

                Comment


                • #9
                  What makes it QuakeC is pretty much all the game related stuff.

                  Touch, think, blocked, anything in ai.qc, movetypes, bounding boxes, the heavy use of vectors to position and move things in 3D space, and other things like that.

                  self.health, self.enemy, CorpseThink, PutClientInServer...

                  it's the stuff it does that makes it QuakeC, plus the fact it is run in a VM in a game engine and utilizes builtins provided by that engine.

                  In short, it is totally geared towards use in a certain kind of game. Other games make use of Lua (Stalker, Crysis) or C++, or have their own scripting language (Doomscript etc).
                  Scout's Journey
                  Rune of Earth Magic

                  Comment


                  • #10
                    Oh wow, so in QuakeC if something equals 0 it will return false in an if statement? Well, why bother to set anything to zero then? Just don't set anything and !power will be automatic, or set power to zero when you declare it at the top.
                    if (power) means anything but zero do this....

                    So in your default: condition if it was another monster type (maybe its another player) power would be 0
                    Code:
                     
                    default:
                    power = 0;
                    break
                    so after that you are gonna plug power in to this portion

                    Code:
                    if ((inflictor != world) && ((targ.movetype == MOVETYPE_WALK)||(targ.movetype == MOVETYPE_STEP))//STEP is for MONSTERS
                    	{
                    		dir = targ.origin - (inflictor.absmin + inflictor.absmax) * 0.5;
                    		dir = normalize(dir);
                    		targ.velocity = targ.velocity + dir*damage*power;
                    	}
                    you should have power set to 1 as a default. Rocket Guy suggests lifting the monster up so theres no ground friction..
                    Last edited by R00k; 02-28-2012, 06:41 PM.
                    www.quakeone.com/qrack | www.quakeone.com/cax| http://en.twitch.tv/sputnikutah

                    Comment


                    • #11
                      Hmmmm, maybe I know more about QuakeC than I admit cause I not only know everything Golden Boy just said, I have written mods that utilize it (and posted them on this forum).

                      As far as what RooK said - I totally had the second part. I'm actually the one that pointed out the missing power var in the math. However, what I didn't know was that the "monster" could potentially even be another player. Although I should have caught this since we are dealing with something that is a walking target. Ok, well then default should actually be 7 then and all power = 7 monsters eliminated (or maybe 5, how resistant do you want quake guy to be?). 1 is too high, even Ogre & shalrath are 4.

                      Oh wait, heres an idea - use a game long map and body count to determine your resistance. Something like

                      Code:
                      resist = floor(  (bodyCount * levelsPassed) *.01 );
                      if (resist <= 8)
                          power = 9 - resist;
                      else
                          power = 1;
                      Last edited by MadGypsy; 02-28-2012, 09:12 PM.
                      http://www.nextgenquake.com

                      Comment


                      • #12
                        Dont forget Quad could be a factor, and throw a wrench in the whole "Al Gore Rhythm"
                        www.quakeone.com/qrack | www.quakeone.com/cax| http://en.twitch.tv/sputnikutah

                        Comment


                        • #13
                          I had to google that phrase, r00k, and I ended up here:The Al Gore Rhythm. I just don't know about you any more.

                          Comment


                          • #14
                            I thought it was just a play on algorithm
                            http://www.nextgenquake.com

                            Comment


                            • #15
                              Zop i was just making a pun on how Al G. invented everything just before his campaign, nothing more to the effort...

                              __________________________________________________ _______
                              doc, the voices have stop talking to me, now they are leaving txt msgs!
                              www.quakeone.com/qrack | www.quakeone.com/cax| http://en.twitch.tv/sputnikutah

                              Comment

                              Working...
                              X