Spy Camera!

    This tutorial will let your player in the game place a camera on the wall!  You can switch back and forth from you normal view to the view of the camera, making it very useful for early warnings and intel.

Add in the blue code and remove any pink code.

G_LOCAL.H modifications:

Add the following in after the declaration for void fire_rail();


//PSYKOTIK
void fire_spycam (edict_t *ent);

This is a forward declaration for our fire_spycam function.

Then add the following in at the bottom of the gclient_s structure:


   
//PSYKOTIK
    edict_t        *spycam;
    edict_t        *oldplayer;
    int             spycamtoggle;

ent->client->spycam is the actual camera entity.
ent->client->oldplayer is a copy of you, so you can see yourself in the camera.
ent->client->spycamtoggle tells whether or not you are looking through the camera.

P_WEAPON.C modifications:

Add the following in before the shotgun/supershotgun code:


/*
======================================================================

SPY CAM - PSYKOTIK

======================================================================
*/

void Weapon_SpyCam (edict_t *ent)
{
    if ((ent->client->newweapon) && (ent->client->weaponstate == WEAPON_READY))
    { // we have a new weapon we should switch to, do it
        ChangeWeapon (ent);
        return;
    }

    if (ent->client->weaponstate == WEAPON_ACTIVATING)
    { // activate the weapon
        ent->client->weaponstate = WEAPON_READY;
        ent->client->ps.gunframe = 1;
        return;
    }

    if (ent->client->weaponstate == WEAPON_READY)
    { // weapon is active
        if ( ent->client->latched_buttons & BUTTON_ATTACK)
        { // and we're trying to shoot
            ent->client->latched_buttons &= ~BUTTON_ATTACK;
            if (ent->client->pers.inventory[ent->client->ammo_index])
            { // we have ammo, fire it
                ent->client->ps.gunframe = 1;
                ent->client->weaponstate = WEAPON_FIRING;
            }
            return;
        }
        return;
    }

    if (ent->client->weaponstate == WEAPON_FIRING)
    { // we've fired, place the camera and subtract 75 cells
        fire_spycam (ent);
        ent->client->pers.inventory[ITEM_INDEX(FindItem("Cells"))] -= 75;
        ent->client->weaponstate = WEAPON_READY;
    }
}

 

P_VIEW.C modifications:

Add the following in near the top of the file:



void SpyCamViewEnt (edict_t *ent);

This is a forward declaration for the function that moves the copy of your player that
you see when in the spycam view.

Then, still inside this file, search for this code:


  
if (v[0] < -14)
        v[0] = -14;
    else if (v[0] > 14)
        v[0] = 14;
    if (v[1] < -14)
        v[1] = -14;
    else if (v[1] > 14)
        v[1] = 14;
    if (v[2] < -22)
        v[2] = -22;
    else if (v[2] > 30)
        v[2] = 30;

and replace it with this:


    //PSYKOTIK
    if (ent->client->spycam == NULL || !ent->client->spycamtoggle)
    {
        if (v[0] < -14)
            v[0] = -14;
        else if (v[0] > 14)
            v[0] = 14;
        if (v[1] < -14)
            v[1] = -14;
        else if (v[1] > 14)
            v[1] = 14;
        if (v[2] < -22)
            v[2] = -22;
        else if (v[2] > 30)
            v[2] = 30;
    }
    else {
        VectorSet (v, 0, 0, 0);
        ent->client->ps.pmove.origin[0] = ent->client->spycam->s.origin[0]*8;
        ent->client->ps.pmove.origin[1] = ent->client->spycam->s.origin[1]*8;
        ent->client->ps.pmove.origin[2] = ent->client->spycam->s.origin[2]*8;
        VectorCopy (ent->client->spycam->s.angles, ent->client->ps.viewangles);
    }

This allows the view to be outside the player box whenever we're in the spycam.

Now, at the bottom of the file at the bottom of EndClientServerFrame put this:


    if (ent->client->spycam != NULL && ent->client->spycamtoggle)
        SpyCamViewEnt(ent);

This checks on the copy of the player which you see in the spycam and makes sure that
it is up to date (i.e. with frames, position, etc.).

P_CLIENT.C modifications:

Near the bottom of PutClientInServer right before the gi.linkentity(ent); put this:

    //PSY
    ent->client->spycam = NULL;
    ent->client->spycamtoggle = 0;

This turns off the camera when you respawn.

Now inside ClientThink right after:



    if (level.intermissiontime)
    {
        client->ps.pmove.pm_type = PM_FREEZE;
        // can exit intermission after five seconds
        if (level.time > level.intermissiontime + 5.0
            && (ucmd->buttons & BUTTON_ANY) )
            level.exitintermission = true;
        return;
    }

put this:


 
  if (ent->client->spycam && ent->client->spycamtoggle)
    {
        memset (&pm, 0, sizeof(pm));
        client->ps.pmove.pm_type = PM_FREEZE;
        VectorCopy (client->spycam->s.angles, client->ps.viewangles);
        VectorCopy (pm.viewangles, client->v_angle);
        VectorCopy (pm.viewangles, client->ps.viewangles);
        gi.linkentity (ent);
        return; // no movement while in cam
    }

This copies your view to the view of the spycam, and stops movement, whenever you
have the spycam selected.

G_WEAPON.C modifications:

At the bottom of the file put this:


/*
=================
fire_spycam
=================
*/
//PSY: SNIPERS
void fire_spycam (edict_t *ent)
{
    edict_t *spycam;
    vec3_t wallp, forward;
    trace_t tr;
   
    if (ent->client->spycam)
        return; // cant place more than one

    // Setup "little look" to close wall
    VectorCopy(ent->s.origin,wallp);
   
    // Cast along view angle
    AngleVectors (ent->client->v_angle, forward, NULL, NULL);
   
    // Setup end point
    wallp[0]=ent->s.origin[0]+forward[0]*50;
    wallp[1]=ent->s.origin[1]+forward[1]*50;
    wallp[2]=ent->s.origin[2]+forward[2]*50;
   
    // trace
    tr = gi.trace (ent->s.origin, NULL, NULL, wallp, ent, MASK_SOLID);
   
    // Line complete ? (ie. no collision)
    if (tr.fraction == 1.0)
    {
        gi.cprintf (ent, PRINT_HIGH, "Too far from wall.\n");
        return;
    }
   
    // Hit sky ?
    if (tr.surface)
        if (tr.surface->flags & SURF_SKY)
            return;
       
    // Ok, lets stick one on then ...
    gi.cprintf (ent, PRINT_HIGH, "Spycam attached.\n");

    // spawn the camera
    spycam = G_Spawn ();
    spycam->owner = NULL; // owner is not needed, we're referencing the spycam var in the client struct
    spycam->solid = SOLID_NOT;
   
    // camera cannot move, and set the angles of the camera model
    spycam->movetype = MOVETYPE_NONE;
    vectoangles(tr.plane.normal,spycam->s.angles);

    // set model
    spycam->s.modelindex = gi.modelindex ("models/objects/grenade2/tris.md2");

    // spycam has no size, so you can walk through it
    VectorClear(spycam->mins);
    VectorClear(spycam->maxs);

    // set the origin
    VectorCopy (tr.endpos, spycam->s.origin);
    spycam->s.origin[2] += ent->viewheight;

    spycam->classname = "spycam";

    // update all this mess and show the camera to the world
    gi.linkentity(spycam);
   
    // set up the client's variables
    ent->client->spycam = spycam;
    ent->client->oldplayer = G_Spawn();
}

void SpyCamViewEnt (edict_t *ent) {
    gclient_t *cl;

    // create a client so you can pick up items/be shot/etc while in spycam
    if (!ent->client->oldplayer->client) {
        cl = (gclient_t *)
            malloc(sizeof(gclient_t));
        ent->client->oldplayer->client = cl;
    }
   
    // copy over all important player data
    if (ent->client->oldplayer) {
        ent->client->oldplayer->s.frame = ent->s.frame;
        VectorCopy (ent->s.origin, ent->client->oldplayer->s.origin);
        VectorCopy (ent->velocity, ent->client->oldplayer->velocity);
        VectorCopy (ent->s.angles, ent->client->oldplayer->s.angles);
        ent->client->oldplayer->s = ent->s;
        gi.linkentity (ent->client->oldplayer);
    }
}

G_ITEMS.C modifications:

At the top below all of the other Weapon_* declarations put this:


void Weapon_SpyCam (edict_t *ent);

 This is a forward declaration for the function in P_WEAPON.C.

Now go down to the itemlist and put this wherever you want to:


/*QUAKED weapon_spycam (.3 .3 1) (-16 -16 -16) (16 16 16)
*/
    {
        "weapon_spycam",
        NULL,
        Use_Weapon,
        NULL,
        Weapon_SpyCam,
        "misc/w_pkup.wav",
        NULL, 0,
        "models/weapons/v_grenade/tris.md2",
/* icon */        "i_fixme",
/* pickup */    "Spycam",
        0,
        75,
        "Cells",
        IT_WEAPON,
        0,
        NULL,
        0,
/* precache */ ""
    },

This defines the actual item.

G_CMDS.C modifications:

Put this right before the ClientCommand function:


void SpyCamKill (edict_t *ent) //PSY: SNIPERS
{
    if (!ent->client->spycam)
        return; // already off

    // kill the toggle, show us our gun
    ent->client->spycamtoggle = 0;
    ent->client->ps.gunindex = gi.modelindex(ent->client->pers.weapon->view_model);
   
    // Added by WarZone - Begin
    ent->svflags &= ~SVF_NOCLIENT;
    ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
    // Added by WarZone - End
   
    // kill the camera ent and the oldplayer ent
    free(ent->client->oldplayer->client);
    G_FreeEdict (ent->client->oldplayer);
    G_FreeEdict (ent->client->spycam);

    // set to null
    ent->client->spycam = NULL;
}

void SpyCamToggle (edict_t *ent)
{
    if (!ent->client->spycamtoggle)
    {
        // we're off, turn it on
        ent->client->ps.gunindex = 0;
        ent->client->spycamtoggle = 1;
        ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
    }
    else
    {
        // we're on, turn it off
        ent->client->ps.gunindex = gi.modelindex(ent->client->pers.weapon->view_model);
        ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
        ent->svflags &= ~SVF_NOCLIENT; // safety's sake =)
        free(ent->client->oldplayer->client);
        G_FreeEdict (ent->client->oldplayer);
        ent->client->spycamtoggle = 0;
    }
}

Now go down right to where it says:

    else    // anything that doesn't match a command will be a chat
        Cmd_Say_f (ent, false, true);

and above that put this:

    // PSYKOTIK: spycam commands
    else if (Q_stricmp(cmd, "killcam") == 0)
        SpyCamKill(ent);
    else if (Q_stricmp(cmd, "spycam") == 0)
        SpyCamToggle(ent);

 There you go! One fully functional spycam.

Tutorial by Psykotik