Convert the railgun into a Telefrag gun.
Code Tutorial - by Zygote (HUMAN DEBRIS)
 

What the hell is a "Telefrag gun"?
We will be modifying the railgun so when you shoot another player, instead of killing them, you will teleport to where they are standing, hence telefrag them. This makes the railgun a lot more fun to use, and creates one hell of an anti camper / anti sniper feature.

The Telefrag gun is included as one of the 10 styles of Instagib in Human Debris mod OSK:Arena

Note: I am not the worlds greatest programmer by a long shot, so don't go crazy if you see a bit of bad programming in this tutorial. I would appreciate any suggested improvements or corrections you can offer. The code has been tested extensively and works fine.

In doing this tutorial I assume you know how to compile the source, and have played with the code before.

Okay, on with the tutorial. This is a server side modification.

Files to be modified
g_local.h
g_misc.c
g_weapon.c

Outline
This is a reasonably simple operation. First we will have to create a new TelefragPlayer function, which is a slightly modified version of the TeleportPlayer function in g_misc.c. Then we will change the DO-WHILE in the weapon_railgun_fire function in g_weapon.c to call our new TelefragPlayer function. We will change the Railgun so that the projectile it fires will not pass through multiple players.

g_local.h
Go down to about halfway and find:
//
// g_misc.c
//
void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles );

Now add this line:
void TelefragPlayer( gentity_t *player, vec3_t origin ); // same as above without the angles
 

g_misc.c
Go down about 1/4 of the way until you find the following:
void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ) {
 gentity_t *tent;

 // use temp events at source and destination to prevent the effect
 // from getting dropped by a second player event
 if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  tent = G_TempEntity( player->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
  tent->s.clientNum = player->s.clientNum;

  tent = G_TempEntity( origin, EV_PLAYER_TELEPORT_IN );
  tent->s.clientNum = player->s.clientNum;
 }

 // unlink to make sure it can't possibly interfere with G_KillBox
 trap_UnlinkEntity (player);

 VectorCopy ( origin, player->client->ps.origin );
 player->client->ps.origin[2] += 1;

 // spit the player out
 AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
 VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity );
 player->client->ps.pm_time = 160;  // hold time
 player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;

 // toggle the teleport bit so the client knows to not lerp
 player->client->ps.eFlags ^= EF_TELEPORT_BIT;

 // set angles
 SetClientViewAngle( player, angles );

 // kill anything at the destination
 if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  G_KillBox (player);
 }

 // save results of pmove
 BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );

 // use the precise origin for linking
 VectorCopy( player->client->ps.origin, player->r.currentOrigin );

 if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  trap_LinkEntity (player);
 }
}

This is the TeleportPlayer function. This function is called when a player uses the Self Teleport Item, and when a player goes through a teleporter. We will be using this as a base for our new TelefragPlayer() function.
After the end of the above code, put the following:

void TelefragPlayer( gentity_t *player, vec3_t origin ) { // removed angles
 gentity_t *tent;

 // use temp events at source and destination to prevent the effect
 // from getting dropped by a second player event
 if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  tent = G_TempEntity( player->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
  tent->s.clientNum = player->s.clientNum;

  tent = G_TempEntity( origin, EV_PLAYER_TELEPORT_IN );
  tent->s.clientNum = player->s.clientNum;
 }

 // unlink to make sure it can't possibly interfere with G_KillBox
 trap_UnlinkEntity (player);

 VectorCopy ( origin, player->client->ps.origin );
 player->client->ps.origin[2] += 1;

 // Zygote
 // Remove angles and "spit-out"
 /*
 // spit the player out
 AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
 VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity );
 player->client->ps.pm_time = 160;  // hold time
 player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
 */
 // toggle the teleport bit so the client knows to not lerp
 player->client->ps.eFlags ^= EF_TELEPORT_BIT;

 // Zygote
 // Remove angles
/*
 // set angles
 SetClientViewAngle( player, angles );
*/
 // kill anything at the destination
 if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  G_KillBox (player);
 }

 // save results of pmove
 BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );

 // use the precise origin for linking
 VectorCopy( player->client->ps.origin, player->r.currentOrigin );

 if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
  trap_LinkEntity (player);
 }
}

You should notice, that it is an exact copy of TeleportPlayer.
All that has been done is block comment (/* */) all code that changes the players angles after the teleport. The reasons is we want the player to be looking in exactly the same place before and after the teleport.
You will also notice we have commented the "spit-out" code. What the "spit-out" code did was scale the players velocity forward in the direction they are looking, and hold it for 160ms. This we commented because after test playing, the "spit-out" becomes increasingly annoying.
 

g_weapon.c
Go down to about halfway, and find the following code:
/*
=================
weapon_railgun_fire
=================
*/
#define MAX_RAIL_HITS 4
void weapon_railgun_fire (gentity_t *ent) {

This marks the start of the weapon_railgun_fire function. This code is called when a player fires the railgun.
I have broken this section up into 2 parts:

Stop the projectile when it hits a player
The railgun projectile can go through up to four players on one shot. We want to change it so the projectile will stop after hitting one player.

Find the following line:
#define MAX_RAIL_HITS 4

Change it to:
#define MAX_RAIL_HITS 1

Adding the Teleport to the Telefrag
Find the following line near the top of the function:
 damage = 100 * s_quadFactor;

Change it to:
 damage = 1000 * s_quadFactor;
This just makes the railgun's projectile hurt a lot more!

Find the following code inside the DO-WHILE:
   if ( traceEnt->takedamage ) {
   if( LogAccuracyHit( traceEnt, ent ) ) {
    hits++;
   }
   G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
  }

What this code does is, when the projectile hits something that can be damaged, increment AccuracyHits and damage what was hit.
We want to change this bit of code to the following:
  if ( traceEnt->takedamage ) {
   if( LogAccuracyHit( traceEnt, ent ) ) {
    hits++;
   }
   // make sure you are a client and you are alive and not a spectator
   if ((traceEnt->client) && (traceEnt->client->ps.pm_type != PM_DEAD) && (traceEnt->client->sess.sessionTeam != TEAM_SPECTATOR)) {
    if ( OnSameTeam (traceEnt, ent)  ) { // if the attacker was on the same team
     if ( !g_friendlyFire.integer ) { // if TF_NO_FRIENDLY_FIRE is set
      G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
      break; // get out of here!
     }
    }
    // Damage then teleport (kill box causes a strange sound!?!?)
    G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
    TelefragPlayer(ent, traceEnt->r.currentOrigin); // teleport player
    break;
   }
   // For Doors, Spectators, Deadbodies...
   G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
  }
 

Analysis:
You can see that we are doing a lot of checking on the entity that the projectile hits. You could rewrite the checking a few different ways if you want, and make it more stream lined.

Is the entity a client, alive, and not a spectator?
 if ((traceEnt->client) && (traceEnt->client->ps.pm_type != PM_DEAD) && (traceEnt->client->sess.sessionTeam != TEAM_SPECTATOR)) {

Is the entity on the same team?
  if ( OnSameTeam (traceEnt, ent)  ) {

The entity is on the same team and Friendly Fire is ON (can damage team mates), Damage it normally, and break out of the DO-WHILE
   if ( !g_friendlyFire.integer ) { // if TF_NO_FRIENDLY_FIRE is set
      G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
      break; // get out of here!
     }
    }

The entity is not on the same team. Damage it, THEN teleport to where the entity was standing, and break out of the DO-WHILE
    G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);
    TelefragPlayer(ent, traceEnt->r.currentOrigin); // teleport player
    break;
   }
The reason why we don't let TelefragPlayer dish out the damage with its call to G_KillBox, is because it causes some sort of strange error noise. As well as that, there is a chance the server could crash if two players try and teleport to the one place at the exact same time. This method (Damage-then-Teleport) is cleaner, and looks exactly the same in game.

The entity is not alive or is not a client, or is a spectator, Damage it, and break out of the DO-WHILE
 // For Doors, Spectators, Deadbodies...
   G_Damage (traceEnt, ent, ent, forward, trace.endpos, damage, 0, MOD_RAILGUN);

The reason why we have to do all of that checking is because when you damage an entity normally with G_Damage(), it does all of the checking for you. Our new TelefragPlayer() does not do any checking, so we have to do it all in advance.
Another way to handle this would be to add all the checking into the TelefragPlayer() function.
 

Thats It!
That is all there is to it.
Compile the code. Now load up your favorite level with a railgun in it (mine is q3tourney3), and take it for a test toast!