
I got the idea after reading the lavacam page.
This ain't up to the quality of lavacam, but does allow modification so that
you can have map entities that allow normal players to use them, al'la Duke
nuke'em. I'm not going to tell you how to that, but you should be able to
puzzle it out. Currently this will hide the player who is using the cameras.
(This is partially designed to that when a large network game is run at my
university, we can have a machine linked up to a projection screen). There is
also the possibility for team cameras, so each team can check the cameras
incomming to their base. Great for a team commander :). I'll be using bits of
code from Psykotiks spycam, but you don't need to have done it. All the camera
data will be stored in a file separate from the bsp. I'll also be copying stuff
from the item placement tut as the principals I'm using are similar, the
parsing will be different though.
First of all we need a few variable set up,
including a structure to contain the camera information. The cameras I'm desiging
will rotate a set amount in the x and y planes. This will be set at the console
(The settings will be retained and reused unless reset). Create a file called
camera.h (If you are lazy, just download the one in the resources section of
the page). Copy the code below into it
typedef struct newcamera_s{ vec3_t origin; vec3_t angles; float h_arc; float h_speed; float v_arc; float v_speed; edict_t *cam; struct newcamera_s *next;} newcamera_t;extern newcamera_t *newcamera_head;extern qboolean cam_erasing;void Cmd_SetCam_f(edict_t *ent);qboolean addcamera(edict_t *ent);qboolean showcamera(vec3_t origin,vec3_t angles,float h_arc,float h_speed,float v_arc,float v_speed);void Cmd_Clearcameras_f(edict_t *ent);void Cmd_Undocamera_f(edict_t *ent);void Cam_Die(edict_t *ent);void CamThink(edict_t *ent);void Cmd_Savecameras_f(edict_t *ent);qboolean savecameradata(edict_t *ent);qboolean loadcameradata(void);void CamCycleHandler(edict_t *ent);
Now, as we are going to be using this within the client
structre (so we know what camera a client is looking through), we will need to
add camera.h to g_local.h just after #include "game.h" Just add
#include "camera.h" on the line just after it.
Next , go to the end of the client structure (just after
qboolean update_chase;and before the }) and add the code below. It should be
fairly self explanitory.
// Camera stuff int camtoggle; newcamera_t *camwatch; int camcycle; int h_arc; int h_speed; int v_arc; int v_speed;
also just before the last } in g_local.h, paste in the
folowing code
// Camera Stuff int h_dir; int v_dir; newcamera_t *camowner;
This allows a cam entity to link back to it's owner. It's
used in the think for the cameras. The dirs are used to control the direction
of travel for the cameras.
I'm not even going to tell you to paste the code for
cameras.c into a file. Just download it. Read though it, it should be fairly
easy to understand. Add it to your makefile. If you have done the item
placement tutorial then you will hit a small problem, I'm using the same
function in both this and the item placement , to deal with it , just protoype
the function at the end of g_local.h (after the }) and delete one copy of it.
That will make it work.
Add this code to the end of putclientinserver (p_client.c)
Its just some initilization code
ent->client->camwatch = NULL; ent->client->camtoggle = 0; ent->client->h_arc=50; ent->client->h_speed=5; ent->client->v_arc=50; ent->client->v_speed=5;
Add the code below to ClientThink (again in p_client.c) It
just freezes the player so they cant move while looking though a camera. Add it
just after the if handling level.intermissiontime
if (ent->client->camwatch && ent->client->camtoggle) { memset (&pm, 0, sizeof(pm)); client->ps.pmove.pm_type = PM_FREEZE; VectorCopy (client->camwatch->cam->s.angles, client->ps.viewangles); // not sure what the next 3 line are for. They were used in a VectorCopy (pm.viewangles, client->v_angle); // similar piece of code. execution time will be barley touched VectorCopy (pm.viewangles, client->ps.viewangles); gi.linkentity (ent); return; // no movement while in cam }
Ok , next we need to change SV_CalcViewOffset (p_view.c)
comment out the code below
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;
insert this code just below the code you commented out
if ((!(ent->client->camwatch == NULL)) || ent->client->camtoggle){VectorSet (v, 0, 0, 0); ent->client->ps.pmove.origin[0] = ent->client->camwatch->cam->s.origin[0]*8; ent->client->ps.pmove.origin[1] = ent->client->camwatch->cam->s.origin[1]*8; ent->client->ps.pmove.origin[2] = ent->client->camwatch->cam->s.origin[2]*8; VectorCopy (ent->client->camwatch->cam->s.angles, ent->client->ps.viewangles); } else {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;}
Add these commands to the end of the command parser in
g_cmds.c just before the final else
else if (Q_stricmp(cmd,"camwatch")==0){ if (ent->client->camtoggle){ ent->client->camtoggle=0; ent->client->camwatch=NULL; } else { if (newcamera_head){ ent->client->camtoggle=1; ent->client->camwatch=newcamera_head; } } } else if (Q_stricmp(cmd,"camnext")==0){ if (ent->client->camtoggle){ if (ent->client->camwatch->next==NULL) { ent->client->camwatch=newcamera_head; } else { ent->client->camwatch=ent->client->camwatch->next; } } } else if (Q_stricmp(cmd,"camcycle")==0){ if (ent->client->camcycle) ent->client->camcycle=0; else ent->client->camcycle=1; } else if (edititems->value){ if (Q_stricmp (cmd, "placecamera") == 0) { if (addcamera(ent)) gi.cprintf(ent, PRINT_HIGH, "Camera placed at (%d,%d,%d)\n",(int)ent->s.origin[0], (int)ent->s.origin[1], (int)ent->s.origin[2]); else gi.cprintf(ent, PRINT_HIGH, "Could not place camera\n",cmd); return; // We messed up cmd } else if (Q_stricmp (cmd, "setcam") == 0) { Cmd_SetCam_f(ent); } else if (Q_stricmp (cmd, "clearcameras") == 0) { Cmd_Clearcameras_f(ent); } else if (Q_stricmp (cmd, "undocameras") == 0) { Cmd_Undocamera_f(ent); } else if (Q_stricmp (cmd, "erasecameras") == 0) { if (cam_erasing) { cam_erasing=0; gi.cprintf(ent, PRINT_HIGH, "New item eraser disabled!\n"); } else { cam_erasing=1; gi.cprintf(ent, PRINT_HIGH, "Camera eraser enabled!\nCareful where you walk!\n"); } } else if (Q_stricmp (cmd, "savecameras") == 0) { Cmd_Savecameras_f (ent); } }
There are 2 cvars you will need to add to the code
edititems and camcycletime. the first controls if you can add cameras (1 means
you can 0 means you cant). camcycletime controls how long you spend on each
camera before moving to the next. find the line which reads extern cvar_t
*sv_maplist;[in g_local.h) then add these lines just below it
extern cvar_t *camcycletime;extern cvar_t *edititems;
Next open up g_mainc.c and just after the line which reads
cvar_t *sv_maplist; add the lines below
cvar_t *camcycletime;cvar_t *edititems;
Open up g_save.c and find the line filterban = gi.cvar
("filterban", "1", 0); Add the lines below just under it
camcycletime=gi.cvar("camcycletime","50",0);edititems = gi.cvar("edititems", "0", CVAR_SERVERINFO|CVAR_LATCH);
Thats the cvars added. Just change them at the console.I
suppose you could have camcycletime being part of the client structure instead
of a cvar. I just prefer it as a cvar.
add the line below to the end of SpawnEntities function in
g_spawn.c
loadcameradata();
Also , add the line below to SP_worldspawn (g_spawn.c) just
before the end
newcamera_head=NULL;
To make the camera cycling work you need to call the
function that check and increnets the timer. add the line below to the end of
ClientBeginServerFrame (p_client.c)
CamCycleHandler(ent);
.
Finally, create a directory called config within your mod
directory. The camera files will be stored there.
To add a camera, use noclip to get to where you want it to
be. then type placecamera at the console to place a camera pointing where you
are facing. Shoot them to get rid of them (would have it being touch them , but
then you would have to be flying. you can't touch when noclip is on) all the
commands should be self explanetory. Use a negative h_speed to make the camera
go left then right.