Filename: howtoctfmod.txt Description: How I setup some mods for Quake Capture the Flag to allow the flag to follow the player when picked up, and also to make the runes, quad, and pent do the same thing. Reason: So another person who finds Quake C hard can do it with less difficulty that I did. (And because Quake is so darn cool!) Creation date: June 2000 I don't feel like typing out another html file, so this one is plain text. I'm just going to start typing. As of today, I know that the code I've produced probably isn't optimal or the best way to do it, but I'm going to post it here anyway. Rather than rewrite this file, I will probably just append to it as I do improvements, so read it to the end. These days (1999-2000) there are a zillion Quake Capture the Flag servers with all kinds of fancy options mods and hacks. The ctf files that I posted on this page are fairly plain and original to Zoid's release of the CTF mod. When I decided I wanted to add a few things to my server, such as having the flag follow the player around, I found that pretty hard. I had a semester of C programming language and that's similar enough to Quake C that I can stare at it for a few hours and figure out what's going on, but I have no real idea how the big picture of how all these .qc files and the Quake engine works together. To start with, there were two things I had in mind. 1.) I wanted the flag to follow my player around like it does on these servers today. 2.) When a player had a rune power-up, I wanted that to show up above his head. I later decided it might be cool when a player had Quad Damage or the Pentegram of Protection to place a copy of that model on the player's back. The stuff I will talk about is all relative to the source files that go along with the Threewave CTF mod and the source files like I have posted here at http://www.geocities.com/quakeserver149/ Hopefully this site will stay here forever, even if I can't update it or I fall of the face of the earth or something. I'm spending time writing this because in ten years I don't want for some moron like me to have to spend twenty weeks trying to figure out how the hell Quake C code works. (Have you ever wondered how long geocities pages will stay there? Will they expire them after 10 years if they've not been updated? Will they keep them around as long at 100 years? In 1000 years will they be archived somewhere?) To begin with I opened up teamplay.qc and client.qc. Eventually in teamplay.qc I found the function: void() TeamCaptureFlagTouch = This is called anytime a player touches a flag, which might look like a gold or silver key; or it could look like the custom flag model that they later made. Regardless of the appearance, the entity or object is the same either way. It has a bunch of conditionals about what to do if it is touched. Mainly, if a member of the other team touches it, we want to make it disappear --> self.model = string_null; and then become untouchable so we don't keep ontouching it with every game tic. --> self.model = string_null; At the end of this function where it is defined what it will do when it thinks, is the following code: self.think = TeamCaptureFlagSanityCheck; self.nextthink = time + 30; // 30 seconds per check This sanity check was put in place to control the flag. A flag has three choices: // Ok, a flag must be in one of three states // 1. At home base (though we shouldn't be called in that case) // 2. On enemy player // 3. A copy is sitting around somewhere, waiting to be picked up Case 1 & 2, nothing happens. Case 3, something wierd must have happened, we create the flag again by calling the flag spawn routine. So my initial thought was, "How about putting some code in the Sanity Check under part 2 where the player has the flag. As a foreshadow, this whole approach eventually failed for me and I did something else. I'll explain what I tried and why it didn't work. Before I started hacking out code, I went into each of the three areas and put in a brpint statement. So at the top of each it was: bprint("Loop Code Sanity Check Running In At Base\n"); // Test Code bprint("Loop Code Sanity Check Running In On Player\n"); // Test Code bprint("Loop Code Sanity Check Running In World\n"); // Test Code I found that every 30 seconds one of these three popped up during play. "Okay, so I'll just change the think time from 30 seconds down to 0.1 which is ten times per second, then I'll just not make it invisible and with each tic I will set the flag origin equal to the player origin" So I commented out the code that said self.model = string_null; then figured out how to change the origin, or position of the flag and put that code in the sanity check. When I ran it, it didn't work. I relied on my test bprint code to tell me what the heck was going on. The sanity was running at base, even if I had it. Why? Because: // at home base? if (self.model != string_null) { By leaving the flag visible, it had a non-null model value, therefore part 1 of the sanity check ran instead of part 2. Even when I did get some code written that made the flag follow the player, the flag would never show up in its home base after it was captured. Never figured out why compeletely. I spent about a week on this crap then tried something else. With a lot of changes I could have written it to work, I suppose, but I was looking for a simpler answer than that. I wanted to spawn a short lived entity next to a player and have it's starting point and origin dependent on the owner entity. I thought about instances where this happens so I could examine the code. The grenade launcher and rocket launcher came to mind. I tried to find them and follow their code starting in weapons.qc, but the functions there make so many calls to other functions that I couldn't understand it. I found what I was looking for with the grappling hook. In hook.qc there was: /*************\ * W_FireChain * \*************/ void() W_FireChain = Basically, the hook spawns, or begins just about where the player is and its velocity is about 1000 in the direction the player is facing. Though what I shows a flag that follows the player, it's probably not the ideal way to do it. What I wrote is basically this: ***** if the player has the key { set model to the key draw the object just behind and left of the player (on his hip) } wait 0.1 seconds, delete the object, and create it again in the player's new position. ***** I copied and pasted the hook function, changed it, and came up with this: I put it in client.qc void () KeyVanish = { remove (self); }; void() FollowFlag = { local entity key; key = spawn (); key.owner = self; key.solid = SOLID_NOT; key.nextthink = time + 0.1; key.think = KeyVanish; setorigin (key, self.origin + (v_forward*-10) + (v_right*-10) + '0 0 -25' ); if (self.items & IT_KEY1) { setmodel (key, "progs/w_s_key.mdl"); } if (self.items & IT_KEY2) { setmodel (key, "progs/w_g_key.mdl"); } }; In english, I would prefer the code to create the entity only one time (when it is picked up), update its position as long as he carries it, then delete it one time (when it is captured or when he dies). In doing the code I did, I created a copy of the actual key and used that for appearance. This way it didn't screw with any of the code they had written for ctf and such. I had to put the remove function first because qcc.exe doesn't seem to know the function exists if it's placed after the function call. Anyway I put the code above into client.qc. I chose that because of the checkdimlight function. The light function scans the player about every game tic to see if he has a power-up. If he does, it makes him glow. I just made a call to FollowFlag(); in client.qc and it runs all the time. Once I had the flag working, time for runes. I don't think I've played on a server yet that had done this. When someone has a rune, such as double damage, why not make that visible to everyone else. The idea was to place a rune above the player's head. I already had the flag code, so the rune code was just another copy and paste. void() RuneRemove = { remove (self); }; void() RuneFollow = { local entity rune; //This is a copy, not the real rune rune = spawn (); rune.owner = self; rune.solid = SOLID_NOT; rune.nextthink = time + 0.1; //This time needs to be slower than the sys_ticrate rune.think = RuneRemove; setorigin (rune, self.origin + (v_forward*-5) + '0 0 40' ); //ITEM_RUNE1_FLAG = Resist = End1.mdl //ITEM_RUNE2_FLAG = Damage = End2.mdl //ITEM_RUNE3_FLAG = Haste = End3.mdl //ITEM_RUNE4_FLAG = Regen = End4.mdl if (self.player_flag & ITEM_RUNE1_FLAG) { setmodel (rune, "progs/end1.mdl"); } if (self.player_flag & ITEM_RUNE2_FLAG) { setmodel (rune, "progs/end2.mdl"); } if (self.player_flag & ITEM_RUNE3_FLAG) { setmodel (rune, "progs/end3.mdl"); } if (self.player_flag & ITEM_RUNE4_FLAG) { setmodel (rune, "progs/end4.mdl"); } }; I know a better if statement structure exists but I don't know how to write it. As is it has to waste time and process each and every single one. Otherwise it could stop at the first test of true. I did the same type of the thing to get the quad and pent power-ups to show up behind the player. void() QuadRemove = { remove (self); }; void() QuadFollow = { local entity quad; //This is a copy, not the real Quad if (self.items & IT_QUAD) { quad = spawn (); quad.owner = self; quad.solid = SOLID_NOT; quad.nextthink = time + 0.1; //This time needs to be slower //than the sys_ticrate quad.think = QuadRemove; setorigin (quad, self.origin + (v_forward*-30) + '0 0 0' ); //-10 or -14 is a good number for his back makevectors(self.v_angle); setmodel (quad, "progs/quaddama.mdl"); } if (self.items & IT_INVULNERABILITY) { quad = spawn (); quad.owner = self; quad.solid = SOLID_NOT; quad.nextthink = time + 0.1; //This time needs to be slower // than the sys_ticrate quad.think = QuadRemove; setorigin (quad, self.origin + (v_forward*-30) + '0 0 0' ); //-10 or -14 is a good number for his back makevectors(self.v_angle); setmodel (quad, "progs/invulner.mdl"); } return; }; This places Quad about 2 feet behind the player's back while he has quad. Once again it's just a copy, not the actual quad entity. I had wanted to place the quad right up against his back and not have it spin around like they do, but from what I'm told right now, the spinning is a property of the model, not of the entity. If I put it too close to the player it spins around in front of his eyes and that is real annoying. At first I thought I would have to write two separate functions to control quad and pent because they would be different entities. It turns out that if you have both quad and pent, then they both show up together. I had assumed that pent would override the quad model and that is all you would see. I was content to let pent trump quad for visibility, but I was lucky that they both show up. I don't suppose it will be all that long before I conjure up some better code for that. That's it for now. =======================================================================