
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