Quake DeveLS - Portable Teleporter

Author: Chris Fry
Difficulty:
Easy

Hello again. This is now my second tutorial. Does anyone else remember the Killer Quake Patch for Quake I or just me? I remember the Portable teleporter in it, and I found it VERY useful - so useful in fact, that after a while, I found it annoying to play without it, as I kept trying to teleport, and couldn't. Anyway, I liked it enough, that I decided to write it in for Quake II. I haven't seen any other patches for one, so I had to write it myself from scratch. I encorporated all the stuff from the Killer Quake one into here, but it's completely original code - I designed it all myself.

The PT allows you to set a destination, then you can teleport back to it as often as you like, so long as you are above 30 health (this stops you teleporting out in melee situations), and you have 10 cells - after all, it can't be free or people would just keep teleporting out of harms way, and nothing would ever get done :) Also, although it took me a while to figure out how, it now telefrags anyone you teleport into :)))


Tutorial

This is a simple tutorial which only needs a couple of changes to a couple of files. First off, we need to add a couple of variables and constants to the "g_local.h" file again. We need to add some constants for ammo and such, so just paste in the following code anywhere in the file, although, if you read my last tutorial, you'll know I like it about half-way down - this is just my preference, and I don't think it matters.


#define TELEPORT_AMMO				10
#define TELEPORT_HEALTH				30

Also, at the end of the same file, we need to add a couple of variables to the 'g_client_s' structure, so it looks like:


	float		pickup_msg_time;

	float		respawn_time;		// can respawn when time > this

	// teleport storage variables
	qboolean	teleport_stored;
	vec3_t		teleport_angles;
	vec3_t		teleport_origin;

Okay, so we've defined all of the variables and stuff that the PT needs, so now we just need the code to make it work. This is all done in the "g_cmds.c" file. First off, go to the bottom of the file, and add the following code. This allows us to type in 'storeteleport' and 'loadteleport' to save a location and then teleport back there.


	else if (Q_stricmp (cmd, "storeteleport") == 0)
		Cmd_Store_Teleport_f (ent);
	else if (Q_stricmp (cmd, "loadteleport") == 0)
		Cmd_Load_Teleport_f (ent);

Now we add the two functions (procedures, whatever, this is C right? no procedures? bugger, still can't figure it out, what's a null returning function called? is it still a procedure?), Cmd_Store_Teleport_f, and Cmd_Load_Teleport_f. Put these in the same file, above the 'Cmd_Say_f' function / procedure.


/*
==================
Cmd_Store_Teleport_f
==================
*/
void Cmd_Store_Teleport_f (edict_t *ent)
{
	VectorCopy (ent->s.origin, ent->client->teleport_origin);
	VectorCopy (ent->s.angles, ent->client->teleport_angles);

	ent->client->teleport_stored = true;

	gi.centerprintf (ent, "Teleport Location Stored!\n");
}

This procedure just copies the current view angles, and current position into the teleport variables defined earlier in this tutorial. It then prints a message in the center of the screen telling you that the teleport location is stored, and sets a variable saying that it has been stored. This is used to stop crashes if a location hasn't been stored. There is probably an easier and more space / memory efficient way of doing this by setting 'teleport_origin' to NULL, or something, but I am sticking with something I KNOW works.


/*
==================
Cmd_Load_Teleport_f
==================
*/
void Cmd_Load_Teleport_f (edict_t *ent)
{
	int		i;

	if (!ent->deadflag)
	{
		if (ent->client->teleport_stored)
		{
			if (ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] < TELEPORT_AMMO)
				gi.centerprintf (ent, "Not enough cells to teleport (need %d).\n", TELEPORT_AMMO);
			else
			{
				if (ent->health < TELEPORT_HEALTH)
					gi.centerprintf (ent, "You can't teleport.\nYou need %d health.\n", TELEPORT_HEALTH);
				else
				{
					gi.WriteByte (svc_temp_entity);
					gi.WriteByte (TE_BOSSTPORT);
					gi.WritePosition (ent->s.origin);
					gi.multicast (ent->s.origin, MULTICAST_PVS);

					// unlink to make sure it can't possibly interfere with KillBox
					gi.unlinkentity (ent);

					VectorCopy (ent->client->teleport_origin, ent->s.origin);
					VectorCopy (ent->client->teleport_origin, ent->s.old_origin);
					ent->s.origin[2] += 10;

					// clear the velocity and hold them in place briefly
					VectorClear (ent->velocity);
					ent->client->ps.pmove.pm_time = 160>>3;		// hold time
					ent->client->ps.pmove.pm_flags |= PMF_TIME_TELEPORT;

					// draw the teleport splash on the player
					ent->s.event = EV_PLAYER_TELEPORT;

					// set angles
					for (i=0 ; i<3 ; i++)
						ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(ent->client->teleport_angles[i] - ent->client->resp.cmd_angles[i]);

					VectorClear (ent->s.angles);
					VectorClear (ent->client->ps.viewangles);
					VectorClear (ent->client->v_angle);

					// kill anything at the destination
					KillBox (ent);

					gi.linkentity (ent);

					ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= TELEPORT_AMMO;
				}
			}
		}
		else
			gi.centerprintf (ent, "You don't have a location stored\n");
	}
	else
		gi.centerprintf (ent, "Sorry. Can't teleport when dead.\n");
}

Hmm.. so thats the big bit. This procedure is the main core of the Portable Teleporter. I must confess to copying alot of this code from the main teleporter entity at the bottom of "g_misc.c". First, this procedure checks to see if we are dead. Of course, we can't teleport when dead - that would just be tooooo pathetic. If we are, it just tells us that we can't teleport. If we are still alive, then we next check that we have a stored teleport location. We just check the 'teleport_stored' variable, and if it is false, we again stick a message up in the center of the screen saying that there isn't a location stored.

The next check we make is whether we have enough cells to teleport. If we don't then we tell the user how many cells he or she needs. I have given it a value of 10 cells, although this can be altered by changing the constant. The last check we make before using the PT, is whether we have enough health to teleport. I just added this for balance. If you could teleport out of a place just before we died, onto say, an adrenaline pack, then it would be just too unfair. To stop this, I won't let people teleport if they are below 30 health. This value can be changed by the other constant defined above.

Okay. Now we KNOW we can teleport, lets do it. The middle bit of code is very similar to the standard Quake 2 teleporter code. I have replaced the 'destination' origin and angle with the stored origin and angle variables. The code first spawns a 'BOSSTPORT' entity where we are currently placed. If you have played to the end of Quake 2, you will have noticed on the 'Inner Chamber' (or BOSS1) level that Makron teleports out with a wierd upwards partical scatter - this is the BOSSTPORT entity.

Next, we unlink the entity, so that it doesn't interfere with the KillBox - more on this later. Now we copy the stored position and angles back to ourselves. To be quite honest, i'm not actually sure what the next bit of code does - I'd be happy if someone could tell me. I gather that it is some sort of 'paralysing' code that stops you from moving immediately after teleportation. The next bit just makes the player 'sparkle' like when you normally teleport - you know, the white sort of cloud that appears when you arrive somewhere.

The next bit is some sort of 'touching up' code - i think it just copies the current view angle to the main movement variables, so that they are not immediately overwritten by momentum. i.e - you know when walking forwards, it takes a tiny while to stop moving when you let go of the controls, I think this would put you back onto the spot you teleported from, if you teleport, but am not sure. If this sounds like gibberish, it probably is :) If anyone REALLY knows what this is, please tell me - it probably really doesn't do anything significant at all. After this, I think it clears the current viewing angle, after it copies it to those movement variables. All I do know, is that it makes you look directly forward in the STORED (not Current) direction, and not up or down.

Finally, we use 'KillBox' which telefrags any entity in your current location - except for you, which is why we unlink you at the beginning, or you would telefrag too - not quite as useful as we might like :) . Then, we just re-link ourselves, and take away the required amount of Cells for the teleport.


Well, thats the end of this second tutorial. If you have any suggestions and/or comments, please contact me. Sorry for that rambling bit of text at the end of the tutorial, especially if it is completely wrong (as I suspect parts of it are - oh well, I'm not perfect by any means). Thanks, and I hope to put up a couple more tutorials possibly in the future sometime - although I have no idea what about. I'm sure I can come up with something :)

Chris Fry

This site, and all content and graphics displayed on it,
are Šopyrighted to the Quake DeveLS team. All rights received.
Got a suggestion? Comment? Question?Hate mail? Send it to us!
Oh yeah, this site is best viewed in 16 Bit or higher, with the resolution on 800*600.
Thanks to Planet Quake for their great help and support with hosting.
Best viewed with Netscape 4