Created By: Kryten
eMail: kryten@inside3d.com
Difficulty Scale: Easy


Here is a nice self teleporter for Quake2. It works best in deathmatch, but can also be used in SP. But beware in some cases in SP you could end up teleporting to a worse place then where you started! This teleporter teleports you to a random deathmatch spot. It is set up so with a little work you could make it use the "mark/recall" system, but that code is not provided.

First open the p_client.c and go to about line 106, there you should see these lines in the info_player_deathmatch:
	if (!deathmatch->value)
	{
		G_FreeEdict (self);
		return;
	}
	SP_misc_teleporter_dest (self);
Now delete everything and add this:
	if (deathmatch->value)
		SP_misc_teleporter_dest (self);
That will force Quake2 to keep the deathmatch spots in SP and not delete them. The deathmatch spots are required if the self teleporter will work in single player.

When you spawn into Quake2 you will have 5 teleporters, every time you start a new level or die this number will be reset. To do this go to the bottom of PutClientInServer and add this line:
	client->pers.inventory[ITEM_INDEX(FindItem("Self Teleporter"))] = 5;
Ok you are now done with the p_client.c file. Now move onto the g_items.c file. At the about line 33 you want to add:
void Use_Teleporter (edict_t *ent, gitem_t *item);
Now go down until you get to the gitem_t itemlist[]. Right after these lines:
	//
	// POWERUP ITEMS
	//
Add this:
/*QUAKED item_tele
*/
	{
		"item_tele",
		NULL,
		Use_Teleporter,
		NULL,
		NULL,
		"items/pkup.wav",
		NULL, 0,
		NULL,
		"p_tele",
		"Self Teleporter",
		0,
		1,
		NULL,
		IT_POWERUP,
		0,
		NULL,
		0,
		""
	},
Now I am not going to lie to you. I don't really know what half that stuff does. I have all that I need here. Some of the NULL's keep the item from being placed in a map or dropped. The "p_tele" refers to the icon to use in the HUD, I have provided this and you do need it so it can be selected in the inventory. Now go all the way to the bottom of this file, there we will add the code to teleport the player. Start by adding this at the bottom:
/*
=================================================================================

Personal Teleporter tutorial

=================================================================================
*/
edict_t *SelectRandomDeathmatchSpawnPoint (void);

void teleport_me (edict_t *ent, edict_t	*dest)
{
	int		i;

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

	// draw the teleport splash at dest 
	gi.WriteByte (svc_temp_entity);		// this is a really cool teleporter effect.
	gi.WriteByte (TE_BOSSTPORT);			// since we are moving the player
	gi.WritePosition (ent->s.origin);		// we cant use the other way to 
	gi.multicast (ent->s.origin, MULTICAST_PVS)	// write the teleporter splash,
	dest->s.event = EV_PLAYER_TELEPORT;	// but we can at the dest

	//move the player
	VectorCopy (dest->s.origin, ent->s.origin);
	VectorCopy (dest->s.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;

	// set angles
	for (i=0 ; i<3 ; i++)
	{
		ent->client->ps.pmove.delta_angles[i] = ANGLE2SHORT(dest->s.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);
}
That is the main body of the teleporter code. It is just modified from ids teleporter code. Now below that add this lines:
void Use_Teleporter (edict_t *ent, gitem_t *item)
{
	edict_t	*dest;

// if you wanted to use a "mark/recall" system the replace the above line with that code.
// sorry but you will have to make that code yourself (hint: one way is to drop an entity)
	dest = SelectRandomDeathmatchSpawnPoint();

	ent->client->pers.inventory[ITEM_INDEX(item)]--;
	ValidateSelectedItem (ent);

	teleport_me (ent, dest);
}
That finds a random deathmatch spot then removes one self teleporter from the inventory, and then calls the function above to actually teleport the player. Compile your code and test it out! You are done!