thirtyflightsofloving/game/g_camera.c
Knightmare66 d16b46e3cf Added re-implementation of func_plat2 and func_door2 from rogue to default Lazarus DLL.
Overhauled child entity movement in default Lazarus DLL.
Added bbox versions of various triggers to default Lazarus DLL.
Added level.maptype field to default Lazarus DLL.
Added entity class IDs to default Lazarus DLL.
Incremented savegame version for default Lazarus DLL.
2020-10-27 02:00:05 -04:00

450 lines
No EOL
12 KiB
C

/*
===========================================================================
Copyright (C) 1997-2001 Id Software, Inc.
Copyright (C) 2000-2002 Mr. Hyde and Mad Dog
This file is part of Lazarus Quake 2 Mod source code.
Lazarus Quake 2 Mod source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Lazarus Quake 2 Mod source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Lazarus Quake 2 Mod source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// g_camera.c (func_monitor)
#include "g_local.h"
void camera_off (edict_t *ent)
{
int i;
if (!ent->client)
return;
if (!ent->client->spycam)
return;
if(ent->client->spycam->viewer == ent)
ent->client->spycam->viewer = NULL;
ent->client->spycam->flags &= ~FL_ROBOT;
if(ent->client->spycam->svflags & SVF_MONSTER)
ent->client->spycam->svflags &= ~SVF_NOCLIENT;
VectorCopy(ent->client->camplayer->s.origin,ent->s.origin);
gi.TagFree(ent->client->camplayer->client);
G_FreeEdict (ent->client->camplayer);
// set angles
ent->movetype = MOVETYPE_WALK;
ent->client->ps.pmove.pm_type = PM_NORMAL;
for (i=0 ; i<3 ; i++)
ent->client->ps.pmove.delta_angles[i] =
ANGLE2SHORT(ent->client->org_viewangles[i] - ent->client->resp.cmd_angles[i]);
VectorCopy(ent->client->org_viewangles, ent->client->resp.cmd_angles);
VectorCopy(ent->client->org_viewangles, ent->s.angles);
VectorCopy(ent->client->org_viewangles, ent->client->ps.viewangles);
VectorCopy(ent->client->org_viewangles, ent->client->v_angle);
ent->client->ps.gunindex = gi.modelindex(ent->client->pers.weapon->view_model);
ent->client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
#ifdef KMQUAKE2_ENGINE_MOD
ent->client->ps.rdflags &= ~(RDF_CAMERAEFFECT|RDF_LETTERBOX); // Knightmare- letterboxing
#endif
ent->svflags &= ~SVF_NOCLIENT;
ent->clipmask = MASK_PLAYERSOLID;
ent->solid = SOLID_BBOX;
ent->client->camplayer = NULL;
ent->client->spycam = NULL;
gi.linkentity(ent);
// if we were previously in third person view, restore it
if (tpp->value)
Cmd_Chasecam_Toggle (ent);
#ifdef KMQUAKE2_ENGINE_MOD
// Knightmare- check if client-side chasecam was previously on
if (ent->style & 2)
{
gi.cvar_forceset(CLIENT_THIRDPERSON_CVAR, "1");
ent->style &= ~2;
}
#endif
}
void faker_animate(edict_t *self)
{
if(self->s.frame < 0 || self->s.frame > 39)
{
self->s.frame = 0;
}
else
{
self->s.frame++;
if(self->s.frame > 39)
self->s.frame = 0;
}
self->nextthink = level.time + FRAMETIME;
gi.linkentity(self);
}
void camera_on (edict_t *ent)
{
gclient_t *cl;
edict_t *faker;
edict_t *monster;
edict_t *camera;
int i;
vec3_t forward, left, up;
if (!ent->client)
return;
if (!ent->client->spycam)
return;
// "viewer" can control camera aim (2nd player to come along and use
// camera cannot)
camera = ent->client->spycam;
if (camera->monsterinfo.aiflags & AI_ACTOR)
camera->flags |= FL_ROBOT;
if (!camera->viewer)
camera->viewer = ent;
// save current viewangles and restore them with camera_off
VectorCopy(ent->client->v_angle,ent->client->org_viewangles);
// copy over all important player data to fake player
ent->client->camplayer = G_Spawn();
faker = ent->client->camplayer;
faker->s.frame = ent->s.frame;
VectorCopy (ent->s.origin, faker->s.origin);
VectorCopy (ent->velocity, faker->velocity);
VectorCopy (ent->s.angles, faker->s.angles);
faker->s = ent->s;
faker->takedamage = DAMAGE_AIM;
faker->movetype = MOVETYPE_WALK;
faker->groundentity = ent->groundentity;
faker->viewheight = ent->viewheight;
faker->inuse = true;
faker->classname = "camplayer";
faker->mass = ent->mass;
faker->solid = SOLID_BBOX;
faker->deadflag = DEAD_NO;
faker->clipmask = MASK_PLAYERSOLID;
faker->health = ent->health;
faker->light_level = ent->light_level;
faker->think = faker_animate;
faker->nextthink = level.time + FRAMETIME;
VectorCopy(ent->mins,faker->mins);
VectorCopy(ent->maxs,faker->maxs);
// create a client so you can pick up items/be shot/etc while in camera
cl = (gclient_t *) gi.TagMalloc(sizeof(gclient_t), TAG_LEVEL);
memset(cl,0,sizeof(gclient_t));
ent->client->camplayer->client = cl;
ent->client->camplayer->target_ent = ent;
gi.linkentity (faker);
AngleVectors(camera->s.angles,forward,left,up);
VectorMA(camera->s.origin, camera->move_origin[0],forward,ent->s.origin);
VectorMA(ent->s.origin, -camera->move_origin[1],left, ent->s.origin);
VectorMA(ent->s.origin, camera->move_origin[2],up, ent->s.origin);
ent->movetype = MOVETYPE_NOCLIP;
ent->clipmask = 0;
ent->solid = SOLID_NOT;
VectorClear(ent->velocity);
ent->client->ps.gunindex = 0;
ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;
#ifdef KMQUAKE2_ENGINE_MOD
// Knightmare- Camera effect and letterboxing
if (ent->client->monitor->spawnflags & 64)
ent->client->ps.rdflags |= RDF_CAMERAEFFECT;
if (ent->client->monitor->spawnflags & 128)
ent->client->ps.rdflags |= RDF_LETTERBOX;
#endif
ent->svflags |= SVF_NOCLIENT;
// check to see if we're the enemy of any monster. If so, make the
// faker the enemy
for (i=maxclients->value+1, monster=g_edicts+i; i<globals.num_edicts; i++, monster++)
{
if (!monster->inuse) continue;
if (!(monster->svflags & SVF_MONSTER)) continue;
if (monster->enemy == ent) {
monster->enemy = faker;
FoundTarget(monster);
}
}
if (ent->client->spycam->viewmessage)
safe_centerprintf(ent,ent->client->spycam->viewmessage);
}
edict_t *G_FindNextCamera (edict_t *camera, edict_t *monitor)
{
edict_t *next;
if(!monitor->target) return NULL;
// If we already have a camera that's a monster, make it visible now
if(camera && (camera->svflags & SVF_MONSTER))
{
camera->svflags &= ~SVF_NOCLIENT;
gi.linkentity(camera);
}
// First, determine if we're going to use the "count" to get the next camera,
// or just scan through the list of entities. If count for the first camera
// in the map is 0, then we'll just use the map order.
next = G_Find(NULL,FOFS(targetname),monitor->target);
if(!next) return NULL;
if(!next->count) {
if(camera) {
next = camera;
next++;
} else
next = g_edicts;
for ( ; next < &g_edicts[globals.num_edicts] ; next++)
{
if (next == camera)
continue;
if (!next->inuse)
continue;
if (next->deadflag == DEAD_DEAD)
continue;
if (!next->targetname)
continue;
// don't select "inactive" cameras
if (!Q_stricmp (next->classname,"turret_breach") && (next->spawnflags & 16))
continue;
if (!Q_stricmp (next->targetname, monitor->target))
goto found_one;
}
next = g_edicts;
for ( ; next < camera ; next++)
{
if (next == camera)
continue;
if (!next->inuse)
continue;
if (next->deadflag == DEAD_DEAD)
continue;
if (!next->targetname)
continue;
// don't select "inactive" cameras
if (!Q_stricmp (next->classname,"turret_breach") && (next->spawnflags & 16))
continue;
if (!Q_stricmp (next->targetname, monitor->target))
goto found_one;
}
} else {
int which, start;
if(camera) {
which = camera->count+1;
if(which > monitor->count) which = 1;
}
else
which = 1;
start = which;
next = g_edicts+1;
while(1) {
if(next->targetname) {
if(!Q_stricmp(next->targetname,monitor->target)) {
if(next->count == which) {
if(!next->inuse || (next->deadflag == DEAD_DEAD) ||
(!Q_stricmp (next->classname,"turret_breach") && (next->spawnflags & 16)) )
{
next = g_edicts;
which++;
if(which > monitor->count) which=1;
if(which == start) return NULL;
} else
goto found_one;
}
}
}
if(next == &g_edicts[globals.num_edicts-1]) {
next = g_edicts;
which++;
if(which > monitor->count) which = 1;
if(which == start) return NULL;
}
next++;
}
}
return NULL;
found_one:
if(!(monitor->spawnflags & 32) && (next->svflags & SVF_MONSTER))
next->svflags |= SVF_NOCLIENT;
return next;
}
edict_t *G_FindPrevCamera (edict_t *camera, edict_t *monitor)
{
edict_t *prev;
edict_t *newcamera;
if(!monitor->target) return NULL;
// If we already have a camera that's a monster, make it visible now
if(camera && (camera->svflags & SVF_MONSTER))
{
camera->svflags &= ~SVF_NOCLIENT;
gi.linkentity(camera);
}
// First, determine if we're going to use the "count" to get the next camera,
// or just scan through the list of entities. If count for the first camera
// in the map is 0, then we'll just use the map order.
prev = G_Find(NULL,FOFS(targetname),monitor->target);
if(!prev) return NULL;
if(!prev->count)
{
newcamera = NULL;
for (prev = g_edicts ; prev < &g_edicts[globals.num_edicts] ; prev++)
{
if (prev == camera) {
if (newcamera) goto found_one;
continue;
}
if (!prev->inuse)
continue;
if (prev->deadflag == DEAD_DEAD)
continue;
if (!prev->targetname)
continue;
// don't select "inactive" cameras
if (!Q_stricmp (prev->classname,"turret_breach") && (prev->spawnflags & 16))
continue;
if (!Q_stricmp (prev->targetname, monitor->target))
newcamera = prev;
}
goto found_one;
}
else
{
int which, start;
if(camera) {
which = camera->count-1;
if(which <= 0) which = monitor->count;
}
else
which = monitor->count;
start = which;
prev = g_edicts+1;
while(1) {
if(prev->targetname) {
if(!Q_stricmp(prev->targetname,monitor->target)) {
if(prev->count == which) {
if(!prev->inuse || (prev->deadflag == DEAD_DEAD) ||
(!Q_stricmp (prev->classname,"turret_breach") && (prev->spawnflags & 16)))
{
prev = g_edicts;
which--;
if(which <= 0) which=monitor->count;
if(which == start) return NULL;
}
else
{
newcamera = prev;
goto found_one;
}
}
}
}
if(prev == &g_edicts[globals.num_edicts-1]) {
prev = g_edicts;
which--;
if(which <= 0) which=monitor->count;
if(which == start) return NULL;
}
prev++;
}
}
found_one:
if (newcamera) { // Knightmare added- check this pointer!
if (!(monitor->spawnflags & 32) && (newcamera->svflags & SVF_MONSTER))
newcamera->svflags |= SVF_NOCLIENT;
}
return newcamera;
}
void use_camera (edict_t *self, edict_t *other, edict_t *activator)
{
edict_t *target;
if(!activator->client) return;
if(activator->client->spycam) // already using camera
return;
target = G_FindNextCamera(NULL,self);
if(!target) return;
// if currently in thirdperson, turn that sucker off
if(tpp->value && activator->client->chasetoggle)
Cmd_Chasecam_Toggle (activator);
#ifdef KMQUAKE2_ENGINE_MOD
// Knightmare- check for client-side chasecam
if (!deathmatch->value && !coop->value && cl_thirdperson->value)
{
gi.cvar_forceset(CLIENT_THIRDPERSON_CVAR, "0");
activator->style |= 2;
}
#endif
activator->client->spycam = target;
activator->client->monitor = self;
camera_on(activator);
}
void func_monitor_init (edict_t *self)
{
edict_t *camera;
self->count = 0;
camera = NULL;
while( (camera=G_Find(camera,FOFS(targetname),self->target)) != NULL)
self->count++;
if(!self->count)
self->s.effects = 0; // don't animate a func_monitor that has no cameras
}
void SP_func_monitor (edict_t *self)
{
if( !self->target ) {
gi.dprintf("func_monitor without a target at %s\n",vtos(self->s.origin));
G_FreeEdict(self);
return;
}
if (self->spawnflags & 8)
self->s.effects |= EF_ANIM_ALL;
if (self->spawnflags & 16)
self->s.effects |= EF_ANIM_ALLFAST;
gi.setmodel (self, self->model);
self->movetype = MOVETYPE_NONE;
self->solid = SOLID_BSP;
self->use = use_camera;
self->think = func_monitor_init;
self->nextthink = level.time + 2*FRAMETIME;
gi.linkentity(self);
}