Created By: Weasel
eMail: icedwards@iname.com
Difficulty Scale: Easy


Teleport and weapon/s drop mod for quake 2 by Weasel.


TD = Teleport Device

INFO:
This file is named tele.c because most editors support color coding of *.c files, making it easier to follow
this tutorial. .

WHAT'S THE MOD DO
If a player dies he will drop one of each weapon he is carrying.
(unless they are holding the blaster or DF_WEAPONS_STAY)

The player can teleport to a deathmatch spawn point (providing he has picked up a randomly spawning TD).

If a player with a TD teleports, that player will drop one of the weapons he is currently carrying  
(unless they are holding the blaster or DF_WEAPONS_STAY). 

If dmflags = DF_WEAPONS_STAY then players can still use the TD
but they will not drop any weapons on teleport or player death.

The TD will spawn almost randomly and will teleport itself around the level until it is picked up.
If a TD is picked up, it will respawn (probably elsewhere in the level).

The number of teleports awarded by a particular TD depends on it's colour 
red, green, blue or uncoloured  = 1 teleport
combination of 2 colours  = 2 teleports
white  = 3 teleports

A lot, well almost all of the code in w_telething.c is from the CTF code by ZOID. I renamed the
functions to more accurately describe what they do in this mod.

WHY
Teleport:
I saw on Thresh's website that id software may incorporate a similar item to teleport 
a player to a spawn point in Q3A. I thought why not do that in Q2.

Weapons drop:
After you frag a player, why not have him drop all the other weapons he picked up 
in his short miserable life. 

CREDITS
id software
ZOID 
Robert Elsner (for his tutorial at inside3d on new entities)
Heaps of others who have written Q2 tutorials at inside3d and elsewhere on the www.

AFFECTED AND NEW FILES
g_cmds.c g_items.c g_save.c g_spawn.c g_local.h p_client.c
w_teleport.c w_teleport.h w_telething.c w_telething.h

DIFFICULTY
Should be easy if you follow the directions.

NOTES:
Use at your own risk, I am not a programmer, I have only been playing with this code 
for a few days in my spare time.
If you try this mod, do it in a separate directory so if it doesn't work out, it won't destroy other mods you may be working on..
You may use this code or portions of it for another mod, subject to conditions contained in the id
software licence agreement for Quake2.
I have compiled this mod using LCC on Intel architecture, 
your mileage may vary in other development environments.
All copyrights mentioned are the property of their respective owners.
Do not run this mod on a public server unless you are a real programmer and
check it properly. 

Key
// lines STARTING with slashes like this line are original code
// demo.c - all instruction lines will start with a filename like this one (filename is for demonstration only)..
Lines with no slashes is the new stuff you are meant to be copying and pasting, except this Line!
	// slashes out here or further are just comments in the code 


// g_cmds.c - bottom of the file 

	else if (Q_stricmp(cmd, "tele") == 0)
		Cmd_Teleport_f (ent);
//	else if (Q_stricmp(cmd, "playerlist") == 0)
//		Cmd_PlayerList_f(ent);
//	else	// anything that doesn't match a command will be a chat
//		Cmd_Say_f (ent, false, true);
//}

// g_items.c - at line 19 

qboolean Pickup_Teleporter (edict_t *ent, edict_t *other);

// g_items.c - at line 1669 this will put it just below the quad entry in the itemlist

	{
		"item_teleporter",
		Pickup_Teleporter,
		NULL,
		NULL,
		NULL,
		"items/pkup.wav",
		"models/items/keys/pass/tris.md2", EF_ROTATE,
		NULL,
		"k_comhead",
		"Teleporter",
		2,
		60,
		NULL,
		0,//IT_WEAPON, //IT_STAY_COOP|IT_KEY,
		0,
		NULL,
		0,
		"medic/idle.wav"
	},

// g_save.c - about line 10

//field_t fields[] = {
//	{"classname", FOFS(classname), F_LSTRING},
	{"tele", FOFS(tele), F_LSTRING},
	{"tele_no", FOFS(tele_no), F_INT},
//	{"model", FOFS(model), F_LSTRING},

// g_spawn.c - line 599

//	G_FindTeams ();
	InitSpawnTele();
	SetupTeleSpawn();
//	PlayerTrail_Init ();
//}

// g_local.h - line 980. jam this code into the edict_t definition.  

	// DO NOT MODIFY ANYTHING ABOVE THIS, THE SERVER
	// EXPECTS THE FIELDS IN THAT ORDER!

	//================================

	char   *tele;  
	int   tele_no;  

// w_telething.c  - new file

#include "g_local.h"

#define TelePickupRespawnDelay	10	// after pickup how long to wait before respawning
#define TeleIdleTime		30	// time to wait at a spawnpoint before respawning

qboolean telespawn = false;

static void SpawnTele(gitem_t *item, edict_t *spot);
void SetupTeleSpawn(void);
void SetRespawnTele (edict_t *ent, float delay);

static edict_t *FindTeleSpawn(void)
{
	edict_t *spot = NULL;
	int i = rand() % 16;

	while (i--)
		spot = G_Find (spot, FOFS(classname), "info_player_deathmatch");
	if (!spot)
		spot = G_Find (spot, FOFS(classname), "info_player_deathmatch");
	return spot;
}


static void SpawnTeles(edict_t *ent)
{
	gitem_t *tele;
	edict_t *spot;

	if ((tele = FindItemByClassname("item_teleporter")) != NULL &&
		(spot = FindTeleSpawn()) != NULL)
		SpawnTele(tele, spot);
	
}

void InitSpawnTele(void) {
	telespawn = false;
}

void SetupTeleSpawn(void)
{
	edict_t *ent;

	if (telespawn)
		return;

	ent = G_Spawn();
	ent->nextthink = level.time + 2;
	ent->think = SpawnTeles;
	telespawn = true;
}

void droptofloor (edict_t *ent);  

qboolean Pickup_Teleporter (edict_t *ent, edict_t *other)
{
	int count;

	if (ent->count) 
		count = ent->count;
	else 
		count = ent->tele_no;
	if(count == 0)
		count = 1; // allows for non coloured teleporters
	other->tele_no = count;
	gi.cprintf(other, PRINT_HIGH, "Teleporter charged with %d teleports\n", count);
	gi.sound(ent, CHAN_ITEM, gi.soundindex("medic/idle.wav"), 1, ATTN_NORM, 0);
	SetRespawnTele (ent, TelePickupRespawnDelay);
     	return true;
 }
static void TeleThink(edict_t *tele)
{
	edict_t *spot;

	if ((spot = FindTeleSpawn()) != NULL) {
		SpawnTele(tele->item, spot);
		G_FreeEdict(tele);
	} else {
		tele->nextthink = level.time + TeleIdleTime;
		tele->think = TeleThink;
	}
}

void SetRespawnTele (edict_t *ent, float delay)
{
	ent->flags |= FL_RESPAWN;
	ent->svflags |= SVF_NOCLIENT;
	ent->solid = SOLID_NOT;
	ent->nextthink = level.time + delay;
	ent->think = TeleThink;
	gi.linkentity (ent);

}

static void SpawnTele(gitem_t *item, edict_t *spot)
{
	edict_t	*ent;
	vec3_t	forward, right;
	vec3_t  angles;

	int red, green, blue  = 0;
	int counter = 0;

	ent = G_Spawn();

	ent->classname = item->classname;
	ent->item = item;
	ent->s.effects = item->world_model_flags;

	red 	= rand() %100;
	green 	= rand() %100;
	blue 	= rand() %100;

	ent->s.effects |= EF_COLOR_SHELL;
	ent->s.effects |= EF_ROCKET;
	
	if(blue > 50){
		ent->s.renderfx |= RF_SHELL_BLUE;
		counter += 1;
	}
	if(red > 50){
		ent->s.renderfx |= RF_SHELL_RED;
		counter += 1;
	}
	if(green > 50){
		ent->s.renderfx |= RF_SHELL_GREEN;
		counter += 1;
	}
	ent->count = counter;
	VectorSet (ent->mins, -15, -15, -15);
	VectorSet (ent->maxs, 15, 15, 15);
	gi.setmodel (ent, ent->item->world_model);
	ent->solid = SOLID_TRIGGER;
	ent->movetype = MOVETYPE_TOSS;  
	ent->touch = Touch_Item;
	ent->owner = ent;

	angles[0] = 0;
	angles[1] = rand() % 360;
	angles[2] = 0;

	AngleVectors (angles, forward, right, NULL);
	VectorCopy (spot->s.origin, ent->s.origin);
	ent->s.origin[2] += 16;
	VectorScale (forward, 100, ent->velocity);
	ent->velocity[2] = 300;

	ent->nextthink = level.time + TeleIdleTime;
	ent->think = TeleThink;

	gi.linkentity (ent);
}

// w_telething.h - new file

void InitSpawnTele(void);
static edict_t *FindTeleSpawn(void);
void SetupTeleSpawn(void);
void SP_teleporter_thing (edict_t *self);
qboolean Pickup_Teleporter (edict_t *ent, edict_t *other);


// p_client.c - line 511 in player_die function  REPLACE TossClientWeapon (self); with new function shown below

//		//TossClientWeapon (self);
		Toss_All_Weapons(self);

// p_client.c - line 531 in player_die.

//	// remove powerups
//	self->client->quad_framenum = 0;
//	self->client->invincible_framenum = 0;
//	self->client->breather_framenum = 0;
//	self->client->enviro_framenum = 0;
//	self->flags &= ~FL_POWER_ARMOR;
	self->tele_no = 0; 

// p_client.c - line 1274 in ClientBeginDeathmatch.

//	gi.bprintf (PRINT_HIGH, "%s entered the game\n", ent->client->pers.netname);
	gi.centerprintf(ent, "Teleport Mod\nTeleporter spawns randomly\n\nbind  tele\n");
//	// make sure all view stuff is valid

// w_teleport.c - new file

#include "g_local.h"

void Toss_All_Weapons (edict_t *self) {

char *weapon_list[] = {"Shotgun", "Super Shotgun", "Machinegun","Chaingun",
	"Grenade Launcher", "Rocket Launcher", "HyperBlaster", "Railgun", "BFG10K", "Grenades"};
int index;

	if ((int)(dmflags->value) & DF_WEAPONS_STAY)
		return;

	for (index=0; index != 10; index++) {
		if(self->client->pers.inventory[ITEM_INDEX(FindItem(weapon_list[index]))]) {
		 	self->client->pers.weapon = FindItem(weapon_list[index]);
			self->client->v_angle[YAW] += 25;
			TossClientWeapon(self);
		}
	}
	
}	

void Spec_Drop_Weapon (edict_t *ent, gitem_t *item)
{
	int		index;

	if ((int)(dmflags->value) & DF_WEAPONS_STAY)
		return;

	index = ITEM_INDEX(item);
	if(index == 1)
		return;
	Drop_Item (ent, item);
	ent->client->pers.inventory[index]--;
}

void Cmd_Teleport_f (edict_t *self) {
	
	edict_t *drop;
	int n; // health
	int i; //counter
	int inventory2[257]; //array of integer for the inventory

	if (!deathmatch->value) {
		gi.cprintf(self, PRINT_HIGH, "You must run server with +set deathmatch 1 to use teleport\n");
		return; // don't wanna do this in single player mode
	}
	if(self->tele_no <1) { // if self-> tele_no is smaller than 1 then you have used all or you don't have the teleporter
		gi.cprintf(self, PRINT_HIGH, "You do not have the teleportation device\n");
		return;
	}

	n = self->health; // get health value

	if (self->client->pers.weapon != FindItem("Blaster"))
		Spec_Drop_Weapon(self, self->client->pers.weapon);

	for (i=1 ; i<=MAX_ITEMS ; i++) {
		inventory2[i] = self->client->pers.inventory[i];
	}
	gi.bprintf (PRINT_HIGH, "%s teleported\n", self->client->pers.netname);
	self->s.event = EV_PLAYER_TELEPORT; // make a teleportation effect

	// if the player manages to get the quad and has the teleporter
	//drop the quad when teleporting, but only if quad drop rule is in use
	if(self->client->quad_framenum > 0)  { // got quad ?
		//if you wanna drop the quad always regardless of server settings delete the line 2 lines below this one and the
		//close bracket } 7 lines down
		if (((int)(dmflags->value) & DF_QUAD_DROP)) { //using quad drop rule
			drop = Drop_Item (self, FindItemByClassname ("item_quad")); 
			drop->spawnflags |= DROPPED_PLAYER_ITEM; 
			drop->touch = Touch_Item; 
			drop->nextthink = level.time + (self->client->quad_framenum - level.framenum) * FRAMETIME;
			drop->think = G_FreeEdict;
		}
	}
	//turn off all power ups, power armor
	self->client->quad_framenum = 0;
	self->client->invincible_framenum = 0;
	self->client->breather_framenum = 0;
	self->client->enviro_framenum = 0;
	self->flags &= ~FL_POWER_ARMOR;
	
	self->s.modelindex2 = 0; 
	self->svflags |= SVF_DEADMONSTER; 
	gi.linkentity (self); 

	PutClientInServer (self); // this line does the respawn and gives 100% health so 
	self->health = n;               // restore health value 

	// copy all the weapons and items back to the players inventory
	for (i=1 ; i<=MAX_ITEMS ; i++) {
		self->client->pers.inventory[i] = inventory2[i];
	}

	// decrement the teleport counter
	self->tele_no--;
	gi.cprintf(self, PRINT_HIGH, "%d teleports remaining\n", self->tele_no);

}

// w_teleport.h - new file

void Toss_All_Weapons (edict_t *self);
void Cmd_Teleport_f (edict_t *self);

// w_teleport.h - END, that's it save and close all files.
// add the new files to the project or makefile and compile

//END OF TUTORIAL, INFORMATION ONLY FROM HERE ON.

Other Notes
If a player gets a TD and doesn't use it, then later on picks up another TD the player 
will not get extra teleports (the number teleports = the number from the most recently 
picked up TD).

If a player gets a TD and Quad or Invulnerability and then teleports, that player will 
teleport however the player will lose the Quad or Invulnerability.