#include "g_local.h" void ChasecamTrack (edict_t *ent); void ClientUserinfoChanged (edict_t *ent, char *userinfo); void ChasecamStart (edict_t *ent) { edict_t *chasecam; ent->client->chasetoggle = 1; ent->client->chaseactive = 1; ent->client->ps.gunindex = 0; chasecam = G_Spawn (); chasecam->owner = ent; chasecam->solid = SOLID_NOT; chasecam->movetype = MOVETYPE_FLYMISSILE; ent->client->ps.pmove.pm_flags |= PMF_NO_PREDICTION; ent->svflags |= SVF_NOCLIENT; ent->s.renderfx |= RF_NOSHADOW; VectorCopy (ent->s.angles, chasecam->s.angles); VectorClear (chasecam->mins); VectorClear (chasecam->maxs); VectorCopy (ent->s.origin, chasecam->s.origin); chasecam->classname = "chasecam"; chasecam->nextthink = level.time + 0.100; chasecam->prethink = ChasecamTrack; ent->client->chasecam = chasecam; ent->client->oldplayer = G_Spawn(); //Knightmare- mark as oldplayer and pointer to client entity ent->client->oldplayer->svflags |= SVF_OLDPLAYER; ent->client->oldplayer->to = ent; // Lazarus reflections // remove reflection of real player, if any DeleteReflection (ent, -1); } void ChasecamRemove (edict_t *ent) { /* Stop the chasecam from moving */ VectorClear (ent->client->chasecam->velocity); ent->client->ps.gunindex = gi.modelindex(ent->client->pers.weapon->view_model); // if (ent->owner->health) ?? SKid ent->s.modelindex = ent->client->oldplayer->s.modelindex; ent->s.modelindex2= ent->client->oldplayer->s.modelindex2; ent->s.modelindex3= ent->client->oldplayer->s.modelindex3; ent->client->ps.pmove.pm_type &= ~PMF_NO_PREDICTION; if(!ent->client->resp.spectator) { ent->svflags &= ~SVF_NOCLIENT; ent->s.renderfx &= ~RF_NOSHADOW; } gi.TagFree(ent->client->oldplayer->client); G_FreeEdict (ent->client->oldplayer); G_FreeEdict (ent->client->chasecam); ent->client->chase_target = NULL; ent->client->chasetoggle = 0; ent->client->chaseactive = 0; } void ChasecamTrack (edict_t *ent) { /* Create tempory vectors and trace variables */ trace_t tr; vec3_t spot1, spot2, dir; vec3_t forward, right, up; int dist; double cap; //mxd. int -> double ent->nextthink = level.time + 0.100; /* get the CLIENT's angle, and break it down into direction vectors, * of forward, right, and up. VERY useful */ AngleVectors (ent->owner->client->v_angle, forward, right, up); /* go starting at the player's origin, forward, ent->chasedist1 * distance, and save the location in vector spot2 */ VectorMA (ent->owner->s.origin, -ent->chasedist1, forward, spot2); /* make spot2 a bit higher, but adding 20 to the Z coordinate */ spot2[2] += 20.000; /* if the client is looking down, do backwards up into the air, 0.6 * to the ratio of looking down, so the crosshair is still roughly * aiming at where the player is aiming. */ if (ent->owner->client->v_angle[0] < 0.000) VectorMA (spot2, (ent->owner->client->v_angle[0] * 0.2), up, spot2); /* if the client is looking up, do the same, but do DOWN rather than * up, so the camera is behind the player aiming in a similar dir */ else if (ent->owner->client->v_angle[0] > 0.000) VectorMA (spot2, (ent->owner->client->v_angle[0] * 0.2), up, spot2); /* make the tr traceline trace from the player model's position, to spot2, * ignoring the player, with MASK_SHOT. These masks have been fixed * from the previous version. The MASK_SHOT will stop the camera from * getting stuck in walls, sky, etc. */ tr = gi.trace (ent->owner->s.origin, NULL, NULL, spot2, ent->owner, MASK_SHOT); /* subtract the endpoint from the start point for length and * direction manipulation */ VectorSubtract (tr.endpos, ent->owner->s.origin, spot1); /* in this case, length */ ent->chasedist1 = VectorLength (spot1); /* go, starting from the end of the trace, 2 points forward (client * angles) and save the location in spot2 */ VectorMA (tr.endpos, 2, forward, spot2); /* make spot1 the same for tempory vector modification and make spot1 * a bit higher than spot2 */ VectorCopy (spot2, spot1); spot1[2] += 32; /* another trace from spot2 to spot2, ignoring player, no masks */ tr = gi.trace (spot2, NULL, NULL, spot1, ent->owner, MASK_SHOT); /* if we hit something, copy the trace end to spot2 and lower spot2 */ if (tr.fraction < 1.000) { VectorCopy (tr.endpos, spot2); spot2[2] -= 32; } /* subtract endpos spot2 from startpos the camera origin, saving it to * the dir vector, and normalize dir for a direction from the camera * origin, to the spot2 */ VectorSubtract (spot2, ent->s.origin, dir); VectorNormalize (dir); /* subtract the same things, but save it in spot1 for a temporary * length calculation */ VectorSubtract (spot2, ent->s.origin, spot1); dist = VectorLength (spot1); /* another traceline */ tr = gi.trace (ent->s.origin, NULL, NULL, spot2, ent->owner, MASK_SHOT); /* if we DON'T hit anyting, do some freaky stuff */ if (tr.fraction == 1.000) { /* Make the angles of the chasecam, the same as the player, so * we are always behind the player. (angles) */ VectorCopy (ent->owner->s.angles, ent->s.angles); /* calculate the percentages of the distances, and make sure we're * not going too far, or too short, in relation to our panning * speed of the chasecam entity */ cap = (dist * 0.400); /* if we're going too fast, make us top speed */ if (cap > 5.200) { ent->velocity[0] = ((dir[0] * dist) * 5.2); ent->velocity[1] = ((dir[1] * dist) * 5.2); ent->velocity[2] = ((dir[2] * dist) * 5.2); } else { /* if we're NOT going top speed, but we're going faster than * 1, relative to the total, make us as fast as we're going */ if ( (cap > 1.000) ) { ent->velocity[0] = ((dir[0] * dist) * cap); ent->velocity[1] = ((dir[1] * dist) * cap); ent->velocity[2] = ((dir[2] * dist) * cap); } else { /* if we're not going faster than one, don't accelerate our * speed at all, make us go slow to our destination */ ent->velocity[0] = (dir[0] * dist); ent->velocity[1] = (dir[1] * dist); ent->velocity[2] = (dir[2] * dist); } } /* subtract endpos;player position, from chasecam position to get * a length to determine whether we should accelerate faster from * the player or not */ VectorSubtract (ent->owner->s.origin, ent->s.origin, spot1); if (VectorLength(spot1) < 20) { ent->velocity[0] *= 2; ent->velocity[1] *= 2; ent->velocity[2] *= 2; } } /* if we DID hit something in the tr.fraction call ages back, then * make the spot2 we created, the position for the chasecamera. */ else VectorCopy (spot2, ent->s.origin); /* If the distance is less than 90, then we haven't reached the * furthest point. If we HAVEN'T reached the furthest point, keep * going backwards. This was a fix for the "shaking". The camera was * getting forced backwards, only to be brought back, next think */ if (ent->chasedist1 < 90.00) ent->chasedist1 += 1; /* if we're too far away, give us a maximum distance */ else if (ent->chasedist1 > 90.00) ent->chasedist1 = 90; /* if we haven't gone anywhere since the last think routine, and we * are greater than 20 points in the distance calculated, add one to * the second chasedistance variable * The "ent->movedir" is a vector which is not used in this entity, so * we can use this a tempory vector belonging to the chasecam, which * can be carried through think routines. */ if (ent->movedir == ent->s.origin) { if (dist > 20) ent->chasedist2++; } /* if we've buggered up more than 3 times, there must be some mistake, * so restart the camera so we re-create a chasecam, destroy the old one, * slowly go outwards from the player, and keep thinking this routing in * the new camera entity */ if (ent->chasedist2 > 3) { ChasecamStart (ent->owner); G_FreeEdict(ent); return; } /* Copy the position of the chasecam now, and stick it to the movedir * variable, for position checking when we rethink this function */ VectorCopy (ent->s.origin, ent->movedir); } void Cmd_Chasecam_Toggle (edict_t *ent) { int i; edict_t *e; //////////////////////////////////// // ADDED if((ent->client->resp.spectator == true))// || //(ent->client->resp.team == CTF_NOTEAM && ent->client->resp.player_class == NO_CLASS)) { if (ent->client->chase_target) { //ent->svflags &= ~SVF_NOCLIENT; //added ent->client->chase_target = NULL; } else { for (i = 1; i <= maxclients->value; i++) { e = g_edicts + i; if (e->inuse && e->solid != SOLID_NOT) { ent->client->chase_target = e; ent->client->update_chase = true; ent->svflags |= SVF_NOCLIENT; //added break; } } } } // Lazarus: Don't allow thirdperson when using spycam else if (!ent->deadflag && !ent->client->spycam /*&& ent->client->resp.player_class*/) { // if (ent->client->chasetoggle) if (ent->client->chaseactive) { // gi.cprintf (ent, PRINT_HIGH, "Leaving Third Person mode.\n"); ChasecamRemove (ent); } // Knightmare- don't use server chasecam if client chasecam is on else if (!cl_thirdperson->value || deathmatch->value || coop->value) { // gi.cprintf (ent, PRINT_HIGH, "Starting Third Person mode.\n"); ChasecamStart (ent); } } } void CheckChasecam_Viewent (edict_t *ent) { // Added by WarZone - Begin gclient_t *cl; if (!ent->client->oldplayer->client) { cl = (gclient_t *) gi.TagMalloc(sizeof(gclient_t), TAG_LEVEL); ent->client->oldplayer->client = cl; } // Added by WarZone - End // if ((ent->client->chasetoggle == 1) && (ent->client->oldplayer)) if ((ent->client->chasetoggle == 1) && (ent->client->oldplayer)) if ((ent->client->chaseactive == 1) && (ent->client->oldplayer)) { ent->client->oldplayer->s.frame = ent->s.frame; /* Copy the origin, the speed, and the model angle, NOT * literal angle to the display entity */ 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); /* Make sure we are using the same model + skin as selected, * as well as the weapon model the player model is holding. * For customized deathmatch weapon displaying, you can * use the modelindex2 for different weapon changing, as you * can read in forthcoming tutorials */ ent->client->oldplayer->s.modelindex = ent->s.modelindex; ent->client->oldplayer->s.modelindex2 = ent->s.modelindex2; ent->client->oldplayer->s.modelindex3 = ent->s.modelindex3; ent->client->oldplayer->s = ent->s; gi.linkentity (ent->client->oldplayer); } }