diff options
Diffstat (limited to 'QW/client/cl_cam.c')
| -rw-r--r-- | QW/client/cl_cam.c | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/QW/client/cl_cam.c b/QW/client/cl_cam.c new file mode 100644 index 0000000..41704f9 --- /dev/null +++ b/QW/client/cl_cam.c @@ -0,0 +1,574 @@ +/* +Copyright (C) 1996-1997 Id Software, Inc. + +This program 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. + +This program 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 included (GNU.txt) GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ +/* ZOID + * + * Player camera tracking in Spectator mode + * + * This takes over player controls for spectator automatic camera. + * Player moves as a spectator, but the camera tracks and enemy player + */ + +#include "quakedef.h" +#include "winquake.h" + +#define PM_SPECTATORMAXSPEED 500 +#define PM_STOPSPEED 100 +#define PM_MAXSPEED 320 +#define BUTTON_JUMP 2 +#define BUTTON_ATTACK 1 +#define MAX_ANGLE_TURN 10 + +static vec3_t desired_position; // where the camera wants to be +static qboolean locked = false; +static int oldbuttons; + +// track high fragger +cvar_t cl_hightrack = {"cl_hightrack", "0" }; + +cvar_t cl_chasecam = {"cl_chasecam", "0"}; + +//cvar_t cl_camera_maxpitch = {"cl_camera_maxpitch", "10" }; +//cvar_t cl_camera_maxyaw = {"cl_camera_maxyaw", "30" }; + +qboolean cam_forceview; +vec3_t cam_viewangles; +double cam_lastviewtime; + +int spec_track = 0; // player# of who we are tracking +int autocam = CAM_NONE; + +static void vectoangles(vec3_t vec, vec3_t ang) +{ + float forward; + float yaw, pitch; + + if (vec[1] == 0 && vec[0] == 0) + { + yaw = 0; + if (vec[2] > 0) + pitch = 90; + else + pitch = 270; + } + else + { + yaw = (int) (atan2(vec[1], vec[0]) * 180 / M_PI); + if (yaw < 0) + yaw += 360; + + forward = sqrt (vec[0]*vec[0] + vec[1]*vec[1]); + pitch = (int) (atan2(vec[2], forward) * 180 / M_PI); + if (pitch < 0) + pitch += 360; + } + + ang[0] = pitch; + ang[1] = yaw; + ang[2] = 0; +} + +static float vlen(vec3_t v) +{ + return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); +} + +// returns true if weapon model should be drawn in camera mode +qboolean Cam_DrawViewModel(void) +{ + if (!cl.spectator) + return true; + + if (autocam && locked && cl_chasecam.value) + return true; + return false; +} + +// returns true if we should draw this player, we don't if we are chase camming +qboolean Cam_DrawPlayer(int playernum) +{ + if (cl.spectator && autocam && locked && cl_chasecam.value && + spec_track == playernum) + return false; + return true; +} + +void Cam_Unlock(void) +{ + if (autocam) { + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, "ptrack"); + autocam = CAM_NONE; + locked = false; + Sbar_Changed(); + } +} + +void Cam_Lock(int playernum) +{ + char st[40]; + + sprintf(st, "ptrack %i", playernum); + MSG_WriteByte (&cls.netchan.message, clc_stringcmd); + MSG_WriteString (&cls.netchan.message, st); + spec_track = playernum; + cam_forceview = true; + locked = false; + Sbar_Changed(); +} + +pmtrace_t Cam_DoTrace(vec3_t vec1, vec3_t vec2) +{ +#if 0 + memset(&pmove, 0, sizeof(pmove)); + + pmove.numphysent = 1; + VectorCopy (vec3_origin, pmove.physents[0].origin); + pmove.physents[0].model = cl.worldmodel; +#endif + + VectorCopy (vec1, pmove.origin); + return PM_PlayerMove(pmove.origin, vec2); +} + +// Returns distance or 9999 if invalid for some reason +static float Cam_TryFlyby(player_state_t *self, player_state_t *player, vec3_t vec, qboolean checkvis) +{ + vec3_t v; + pmtrace_t trace; + float len; + + vectoangles(vec, v); +// v[0] = -v[0]; + VectorCopy (v, pmove.angles); + VectorNormalize(vec); + VectorMA(player->origin, 800, vec, v); + // v is endpos + // fake a player move + trace = Cam_DoTrace(player->origin, v); + if (/*trace.inopen ||*/ trace.inwater) + return 9999; + VectorCopy(trace.endpos, vec); + VectorSubtract(trace.endpos, player->origin, v); + len = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); + if (len < 32 || len > 800) + return 9999; + if (checkvis) { + VectorSubtract(trace.endpos, self->origin, v); + len = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]); + + trace = Cam_DoTrace(self->origin, vec); + if (trace.fraction != 1 || trace.inwater) + return 9999; + } + return len; +} + +// Is player visible? +static qboolean Cam_IsVisible(player_state_t *player, vec3_t vec) +{ + pmtrace_t trace; + vec3_t v; + float d; + + trace = Cam_DoTrace(player->origin, vec); + if (trace.fraction != 1 || /*trace.inopen ||*/ trace.inwater) + return false; + // check distance, don't let the player get too far away or too close + VectorSubtract(player->origin, vec, v); + d = vlen(v); + if (d < 16) + return false; + return true; +} + +static qboolean InitFlyby(player_state_t *self, player_state_t *player, int checkvis) +{ + float f, max; + vec3_t vec, vec2; + vec3_t forward, right, up; + + VectorCopy(player->viewangles, vec); + vec[0] = 0; + AngleVectors (vec, forward, right, up); +// for (i = 0; i < 3; i++) +// forward[i] *= 3; + + max = 1000; + VectorAdd(forward, up, vec2); + VectorAdd(vec2, right, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorAdd(forward, up, vec2); + VectorSubtract(vec2, right, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorAdd(forward, right, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorSubtract(forward, right, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorAdd(forward, up, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorSubtract(forward, up, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorAdd(up, right, vec2); + VectorSubtract(vec2, forward, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorSubtract(up, right, vec2); + VectorSubtract(vec2, forward, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + // invert + VectorSubtract(vec3_origin, forward, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorCopy(forward, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + // invert + VectorSubtract(vec3_origin, right, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + VectorCopy(right, vec2); + if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) { + max = f; + VectorCopy(vec2, vec); + } + + // ack, can't find him + if (max >= 1000) { +// Cam_Unlock(); + return false; + } + locked = true; + VectorCopy(vec, desired_position); + return true; +} + +static void Cam_CheckHighTarget(void) +{ + int i, j, max; + player_info_t *s; + + j = -1; + for (i = 0, max = -9999; i < MAX_CLIENTS; i++) { + s = &cl.players[i]; + if (s->name[0] && !s->spectator && s->frags > max) { + max = s->frags; + j = i; + } + } + if (j >= 0) { + if (!locked || cl.players[j].frags > cl.players[spec_track].frags) + Cam_Lock(j); + } else + Cam_Unlock(); +} + +// ZOID +// +// Take over the user controls and track a player. +// We find a nice position to watch the player and move there +void Cam_Track(usercmd_t *cmd) +{ + player_state_t *player, *self; + frame_t *frame; + vec3_t vec; + float len; + + if (!cl.spectator) + return; + + if (cl_hightrack.value && !locked) + Cam_CheckHighTarget(); + + if (!autocam || cls.state != ca_active) + return; + + if (locked && (!cl.players[spec_track].name[0] || cl.players[spec_track].spectator)) { + locked = false; + if (cl_hightrack.value) + Cam_CheckHighTarget(); + else + Cam_Unlock(); + return; + } + + frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + player = frame->playerstate + spec_track; + self = frame->playerstate + cl.playernum; + + if (!locked || !Cam_IsVisible(player, desired_position)) { + if (!locked || realtime - cam_lastviewtime > 0.1) { + if (!InitFlyby(self, player, true)) + InitFlyby(self, player, false); + cam_lastviewtime = realtime; + } + } else + cam_lastviewtime = realtime; + + // couldn't track for some reason + if (!locked || !autocam) + return; + + if (cl_chasecam.value) { + cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; + + VectorCopy(player->viewangles, cl.viewangles); + VectorCopy(player->origin, desired_position); + if (memcmp(&desired_position, &self->origin, sizeof(desired_position)) != 0) { + MSG_WriteByte (&cls.netchan.message, clc_tmove); + MSG_WriteCoord (&cls.netchan.message, desired_position[0]); + MSG_WriteCoord (&cls.netchan.message, desired_position[1]); + MSG_WriteCoord (&cls.netchan.message, desired_position[2]); + // move there locally immediately + VectorCopy(desired_position, self->origin); + } + self->weaponframe = player->weaponframe; + + } else { + // Ok, move to our desired position and set our angles to view + // the player + VectorSubtract(desired_position, self->origin, vec); + len = vlen(vec); + cmd->forwardmove = cmd->sidemove = cmd->upmove = 0; + if (len > 16) { // close enough? + MSG_WriteByte (&cls.netchan.message, clc_tmove); + MSG_WriteCoord (&cls.netchan.message, desired_position[0]); + MSG_WriteCoord (&cls.netchan.message, desired_position[1]); + MSG_WriteCoord (&cls.netchan.message, desired_position[2]); + } + + // move there locally immediately + VectorCopy(desired_position, self->origin); + + VectorSubtract(player->origin, desired_position, vec); + vectoangles(vec, cl.viewangles); + cl.viewangles[0] = -cl.viewangles[0]; + } +} + +#if 0 +static float adjustang(float current, float ideal, float speed) +{ + float move; + + current = anglemod(current); + ideal = anglemod(ideal); + + if (current == ideal) + return current; + + move = ideal - current; + if (ideal > current) + { + if (move >= 180) + move = move - 360; + } + else + { + if (move <= -180) + move = move + 360; + } + if (move > 0) + { + if (move > speed) + move = speed; + } + else + { + if (move < -speed) + move = -speed; + } + +//Con_Printf("c/i: %4.2f/%4.2f move: %4.2f\n", current, ideal, move); + return anglemod (current + move); +} +#endif + +#if 0 +void Cam_SetView(void) +{ + return; + player_state_t *player, *self; + frame_t *frame; + vec3_t vec, vec2; + + if (cls.state != ca_active || !cl.spectator || + !autocam || !locked) + return; + + frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + player = frame->playerstate + spec_track; + self = frame->playerstate + cl.playernum; + + VectorSubtract(player->origin, cl.simorg, vec); + if (cam_forceview) { + cam_forceview = false; + vectoangles(vec, cam_viewangles); + cam_viewangles[0] = -cam_viewangles[0]; + } else { + vectoangles(vec, vec2); + vec2[PITCH] = -vec2[PITCH]; + + cam_viewangles[PITCH] = adjustang(cam_viewangles[PITCH], vec2[PITCH], cl_camera_maxpitch.value); + cam_viewangles[YAW] = adjustang(cam_viewangles[YAW], vec2[YAW], cl_camera_maxyaw.value); + } + VectorCopy(cam_viewangles, cl.viewangles); + VectorCopy(cl.viewangles, cl.simangles); +} +#endif + +void Cam_FinishMove(usercmd_t *cmd) +{ + int i; + player_info_t *s; + int end; + + if (cls.state != ca_active) + return; + + if (!cl.spectator) // only in spectator mode + return; + +#if 0 + if (autocam && locked) { + frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK]; + player = frame->playerstate + spec_track; + self = frame->playerstate + cl.playernum; + + VectorSubtract(player->origin, self->origin, vec); + if (cam_forceview) { + cam_forceview = false; + vectoangles(vec, cam_viewangles); + cam_viewangles[0] = -cam_viewangles[0]; + } else { + vectoangles(vec, vec2); + vec2[PITCH] = -vec2[PITCH]; + + cam_viewangles[PITCH] = adjustang(cam_viewangles[PITCH], vec2[PITCH], cl_camera_maxpitch.value); + cam_viewangles[YAW] = adjustang(cam_viewangles[YAW], vec2[YAW], cl_camera_maxyaw.value); + } + VectorCopy(cam_viewangles, cl.viewangles); + } +#endif + + if (cmd->buttons & BUTTON_ATTACK) { + if (!(oldbuttons & BUTTON_ATTACK)) { + + oldbuttons |= BUTTON_ATTACK; + autocam++; + + if (autocam > CAM_TRACK) { + Cam_Unlock(); + VectorCopy(cl.viewangles, cmd->angles); + return; + } + } else + return; + } else { + oldbuttons &= ~BUTTON_ATTACK; + if (!autocam) + return; + } + + if (autocam && cl_hightrack.value) { + Cam_CheckHighTarget(); + return; + } + + if (locked) { + if ((cmd->buttons & BUTTON_JUMP) && (oldbuttons & BUTTON_JUMP)) + return; // don't pogo stick + + if (!(cmd->buttons & BUTTON_JUMP)) { + oldbuttons &= ~BUTTON_JUMP; + return; + } + oldbuttons |= BUTTON_JUMP; // don't jump again until released + } + +// Con_Printf("Selecting track target...\n"); + + if (locked && autocam) + end = (spec_track + 1) % MAX_CLIENTS; + else + end = spec_track; + i = end; + do { + s = &cl.players[i]; + if (s->name[0] && !s->spectator) { + Cam_Lock(i); + return; + } + i = (i + 1) % MAX_CLIENTS; + } while (i != end); + // stay on same guy? + i = spec_track; + s = &cl.players[i]; + if (s->name[0] && !s->spectator) { + Cam_Lock(i); + return; + } + Con_Printf("No target found ...\n"); + autocam = locked = false; +} + +void Cam_Reset(void) +{ + autocam = CAM_NONE; + spec_track = 0; +} + +void CL_InitCam(void) +{ + Cvar_RegisterVariable (&cl_hightrack); + Cvar_RegisterVariable (&cl_chasecam); +// Cvar_RegisterVariable (&cl_camera_maxpitch); +// Cvar_RegisterVariable (&cl_camera_maxyaw); +} + + |
