• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2 Copyright (C) 1996-1997 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the included (GNU.txt) GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18 
19 */
20 /* ZOID
21  *
22  * Player camera tracking in Spectator mode
23  *
24  * This takes over player controls for spectator automatic camera.
25  * Player moves as a spectator, but the camera tracks and enemy player
26  */
27 
28 #include "quakedef.h"
29 #include "winquake.h"
30 
31 #define	PM_SPECTATORMAXSPEED	500
32 #define	PM_STOPSPEED	100
33 #define	PM_MAXSPEED			320
34 #define BUTTON_JUMP 2
35 #define BUTTON_ATTACK 1
36 #define MAX_ANGLE_TURN 10
37 
38 static vec3_t desired_position; // where the camera wants to be
39 static qboolean locked = false;
40 static int oldbuttons;
41 
42 // track high fragger
43 cvar_t cl_hightrack = CVAR2("cl_hightrack", "0" );
44 
45 cvar_t cl_chasecam = CVAR2("cl_chasecam", "0");
46 
47 //cvar_t cl_camera_maxpitch = {"cl_camera_maxpitch", "10" };
48 //cvar_t cl_camera_maxyaw = {"cl_camera_maxyaw", "30" };
49 
50 qboolean cam_forceview;
51 vec3_t cam_viewangles;
52 double cam_lastviewtime;
53 
54 int spec_track = 0; // player# of who we are tracking
55 int autocam = CAM_NONE;
56 
vectoangles(vec3_t vec,vec3_t ang)57 static void vectoangles(vec3_t vec, vec3_t ang)
58 {
59 	float	forward;
60 	float	yaw, pitch;
61 
62 	if (vec[1] == 0 && vec[0] == 0)
63 	{
64 		yaw = 0;
65 		if (vec[2] > 0)
66 			pitch = 90;
67 		else
68 			pitch = 270;
69 	}
70 	else
71 	{
72 		yaw = (int) (atan2(vec[1], vec[0]) * 180 / M_PI);
73 		if (yaw < 0)
74 			yaw += 360;
75 
76 		forward = sqrt (vec[0]*vec[0] + vec[1]*vec[1]);
77 		pitch = (int) (atan2(vec[2], forward) * 180 / M_PI);
78 		if (pitch < 0)
79 			pitch += 360;
80 	}
81 
82 	ang[0] = pitch;
83 	ang[1] = yaw;
84 	ang[2] = 0;
85 }
86 
vlen(vec3_t v)87 static float vlen(vec3_t v)
88 {
89 	return sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
90 }
91 
92 // returns true if weapon model should be drawn in camera mode
Cam_DrawViewModel(void)93 qboolean Cam_DrawViewModel(void)
94 {
95 	if (!cl.spectator)
96 		return true;
97 
98 	if (autocam && locked && cl_chasecam.value)
99 		return true;
100 	return false;
101 }
102 
103 // returns true if we should draw this player, we don't if we are chase camming
Cam_DrawPlayer(int playernum)104 qboolean Cam_DrawPlayer(int playernum)
105 {
106 	if (cl.spectator && autocam && locked && cl_chasecam.value &&
107 		spec_track == playernum)
108 		return false;
109 	return true;
110 }
111 
Cam_Unlock(void)112 void Cam_Unlock(void)
113 {
114 	if (autocam) {
115 		MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
116 		MSG_WriteString (&cls.netchan.message, "ptrack");
117 		autocam = CAM_NONE;
118 		locked = false;
119 		Sbar_Changed();
120 	}
121 }
122 
Cam_Lock(int playernum)123 void Cam_Lock(int playernum)
124 {
125 	char st[40];
126 
127 	sprintf(st, "ptrack %i", playernum);
128 	MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
129 	MSG_WriteString (&cls.netchan.message, st);
130 	spec_track = playernum;
131 	cam_forceview = true;
132 	locked = false;
133 	Sbar_Changed();
134 }
135 
Cam_DoTrace(vec3_t vec1,vec3_t vec2)136 pmtrace_t Cam_DoTrace(vec3_t vec1, vec3_t vec2)
137 {
138 #if 0
139 	memset(&pmove, 0, sizeof(pmove));
140 
141 	pmove.numphysent = 1;
142 	VectorCopy (vec3_origin, pmove.physents[0].origin);
143 	pmove.physents[0].model = cl.worldmodel;
144 #endif
145 
146 	VectorCopy (vec1, pmove.origin);
147 	return PM_PlayerMove(pmove.origin, vec2);
148 }
149 
150 // Returns distance or 9999 if invalid for some reason
Cam_TryFlyby(player_state_t * self,player_state_t * player,vec3_t vec,qboolean checkvis)151 static float Cam_TryFlyby(player_state_t *self, player_state_t *player, vec3_t vec, qboolean checkvis)
152 {
153 	vec3_t v;
154 	pmtrace_t trace;
155 	float len;
156 
157 	vectoangles(vec, v);
158 //	v[0] = -v[0];
159 	VectorCopy (v, pmove.angles);
160 	VectorNormalize(vec);
161 	VectorMA(player->origin, 800, vec, v);
162 	// v is endpos
163 	// fake a player move
164 	trace = Cam_DoTrace(player->origin, v);
165 	if (/*trace.inopen ||*/ trace.inwater)
166 		return 9999;
167 	VectorCopy(trace.endpos, vec);
168 	VectorSubtract(trace.endpos, player->origin, v);
169 	len = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
170 	if (len < 32 || len > 800)
171 		return 9999;
172 	if (checkvis) {
173 		VectorSubtract(trace.endpos, self->origin, v);
174 		len = sqrt(v[0]*v[0] + v[1]*v[1] + v[2]*v[2]);
175 
176 		trace = Cam_DoTrace(self->origin, vec);
177 		if (trace.fraction != 1 || trace.inwater)
178 			return 9999;
179 	}
180 	return len;
181 }
182 
183 // Is player visible?
Cam_IsVisible(player_state_t * player,vec3_t vec)184 static qboolean Cam_IsVisible(player_state_t *player, vec3_t vec)
185 {
186 	pmtrace_t trace;
187 	vec3_t v;
188 	float d;
189 
190 	trace = Cam_DoTrace(player->origin, vec);
191 	if (trace.fraction != 1 || /*trace.inopen ||*/ trace.inwater)
192 		return false;
193 	// check distance, don't let the player get too far away or too close
194 	VectorSubtract(player->origin, vec, v);
195 	d = vlen(v);
196 	if (d < 16)
197 		return false;
198 	return true;
199 }
200 
InitFlyby(player_state_t * self,player_state_t * player,int checkvis)201 static qboolean InitFlyby(player_state_t *self, player_state_t *player, int checkvis)
202 {
203     float f, max;
204     vec3_t vec, vec2;
205 	vec3_t forward, right, up;
206 
207 	VectorCopy(player->viewangles, vec);
208     vec[0] = 0;
209 	AngleVectors (vec, forward, right, up);
210 //	for (i = 0; i < 3; i++)
211 //		forward[i] *= 3;
212 
213     max = 1000;
214 	VectorAdd(forward, up, vec2);
215 	VectorAdd(vec2, right, vec2);
216     if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) {
217         max = f;
218 		VectorCopy(vec2, vec);
219     }
220 	VectorAdd(forward, up, vec2);
221 	VectorSubtract(vec2, right, vec2);
222     if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) {
223         max = f;
224 		VectorCopy(vec2, vec);
225     }
226 	VectorAdd(forward, right, vec2);
227     if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) {
228         max = f;
229 		VectorCopy(vec2, vec);
230     }
231 	VectorSubtract(forward, right, vec2);
232     if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) {
233         max = f;
234 		VectorCopy(vec2, vec);
235     }
236 	VectorAdd(forward, up, vec2);
237     if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) {
238         max = f;
239 		VectorCopy(vec2, vec);
240     }
241 	VectorSubtract(forward, up, vec2);
242     if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) {
243         max = f;
244 		VectorCopy(vec2, vec);
245     }
246 	VectorAdd(up, right, vec2);
247 	VectorSubtract(vec2, forward, vec2);
248     if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) {
249         max = f;
250 		VectorCopy(vec2, vec);
251     }
252 	VectorSubtract(up, right, vec2);
253 	VectorSubtract(vec2, forward, vec2);
254     if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) {
255         max = f;
256 		VectorCopy(vec2, vec);
257     }
258 	// invert
259 	VectorSubtract(vec3_origin, forward, vec2);
260     if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) {
261         max = f;
262 		VectorCopy(vec2, vec);
263     }
264 	VectorCopy(forward, vec2);
265     if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) {
266         max = f;
267 		VectorCopy(vec2, vec);
268     }
269 	// invert
270 	VectorSubtract(vec3_origin, right, vec2);
271     if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) {
272         max = f;
273 		VectorCopy(vec2, vec);
274     }
275 	VectorCopy(right, vec2);
276     if ((f = Cam_TryFlyby(self, player, vec2, checkvis)) < max) {
277         max = f;
278 		VectorCopy(vec2, vec);
279     }
280 
281 	// ack, can't find him
282     if (max >= 1000) {
283 //		Cam_Unlock();
284 		return false;
285 	}
286 	locked = true;
287 	VectorCopy(vec, desired_position);
288 	return true;
289 }
290 
Cam_CheckHighTarget(void)291 static void Cam_CheckHighTarget(void)
292 {
293 	int i, j, max;
294 	player_info_t	*s;
295 
296 	j = -1;
297 	for (i = 0, max = -9999; i < MAX_CLIENTS; i++) {
298 		s = &cl.players[i];
299 		if (s->name[0] && !s->spectator && s->frags > max) {
300 			max = s->frags;
301 			j = i;
302 		}
303 	}
304 	if (j >= 0) {
305 		if (!locked || cl.players[j].frags > cl.players[spec_track].frags)
306 			Cam_Lock(j);
307 	} else
308 		Cam_Unlock();
309 }
310 
311 // ZOID
312 //
313 // Take over the user controls and track a player.
314 // We find a nice position to watch the player and move there
Cam_Track(usercmd_t * cmd)315 void Cam_Track(usercmd_t *cmd)
316 {
317 	player_state_t *player, *self;
318 	frame_t *frame;
319 	vec3_t vec;
320 	float len;
321 
322 	if (!cl.spectator)
323 		return;
324 
325 	if (cl_hightrack.value && !locked)
326 		Cam_CheckHighTarget();
327 
328 	if (!autocam || cls.state != ca_active)
329 		return;
330 
331 	if (locked && (!cl.players[spec_track].name[0] || cl.players[spec_track].spectator)) {
332 		locked = false;
333 		if (cl_hightrack.value)
334 			Cam_CheckHighTarget();
335 		else
336 			Cam_Unlock();
337 		return;
338 	}
339 
340 	frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK];
341 	player = frame->playerstate + spec_track;
342 	self = frame->playerstate + cl.playernum;
343 
344 	if (!locked || !Cam_IsVisible(player, desired_position)) {
345 		if (!locked || realtime - cam_lastviewtime > 0.1) {
346 			if (!InitFlyby(self, player, true))
347 				InitFlyby(self, player, false);
348 			cam_lastviewtime = realtime;
349 		}
350 	} else
351 		cam_lastviewtime = realtime;
352 
353 	// couldn't track for some reason
354 	if (!locked || !autocam)
355 		return;
356 
357 	if (cl_chasecam.value) {
358 		cmd->forwardmove = cmd->sidemove = cmd->upmove = 0;
359 
360 		VectorCopy(player->viewangles, cl.viewangles);
361 		VectorCopy(player->origin, desired_position);
362 		if (memcmp(&desired_position, &self->origin, sizeof(desired_position)) != 0) {
363 			MSG_WriteByte (&cls.netchan.message, clc_tmove);
364 			MSG_WriteCoord (&cls.netchan.message, desired_position[0]);
365 			MSG_WriteCoord (&cls.netchan.message, desired_position[1]);
366 			MSG_WriteCoord (&cls.netchan.message, desired_position[2]);
367 			// move there locally immediately
368 			VectorCopy(desired_position, self->origin);
369 		}
370 		self->weaponframe = player->weaponframe;
371 
372 	} else {
373 		// Ok, move to our desired position and set our angles to view
374 		// the player
375 		VectorSubtract(desired_position, self->origin, vec);
376 		len = vlen(vec);
377 		cmd->forwardmove = cmd->sidemove = cmd->upmove = 0;
378 		if (len > 16) { // close enough?
379 			MSG_WriteByte (&cls.netchan.message, clc_tmove);
380 			MSG_WriteCoord (&cls.netchan.message, desired_position[0]);
381 			MSG_WriteCoord (&cls.netchan.message, desired_position[1]);
382 			MSG_WriteCoord (&cls.netchan.message, desired_position[2]);
383 		}
384 
385 		// move there locally immediately
386 		VectorCopy(desired_position, self->origin);
387 
388 		VectorSubtract(player->origin, desired_position, vec);
389 		vectoangles(vec, cl.viewangles);
390 		cl.viewangles[0] = -cl.viewangles[0];
391 	}
392 }
393 
394 #if 0
395 static float adjustang(float current, float ideal, float speed)
396 {
397 	float move;
398 
399 	current = anglemod(current);
400 	ideal = anglemod(ideal);
401 
402 	if (current == ideal)
403 		return current;
404 
405 	move = ideal - current;
406 	if (ideal > current)
407 	{
408 		if (move >= 180)
409 			move = move - 360;
410 	}
411 	else
412 	{
413 		if (move <= -180)
414 			move = move + 360;
415 	}
416 	if (move > 0)
417 	{
418 		if (move > speed)
419 			move = speed;
420 	}
421 	else
422 	{
423 		if (move < -speed)
424 			move = -speed;
425 	}
426 
427 //Con_Printf("c/i: %4.2f/%4.2f move: %4.2f\n", current, ideal, move);
428 	return anglemod (current + move);
429 }
430 #endif
431 
432 #if 0
433 void Cam_SetView(void)
434 {
435 	return;
436 	player_state_t *player, *self;
437 	frame_t *frame;
438 	vec3_t vec, vec2;
439 
440 	if (cls.state != ca_active || !cl.spectator ||
441 		!autocam || !locked)
442 		return;
443 
444 	frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK];
445 	player = frame->playerstate + spec_track;
446 	self = frame->playerstate + cl.playernum;
447 
448 	VectorSubtract(player->origin, cl.simorg, vec);
449 	if (cam_forceview) {
450 		cam_forceview = false;
451 		vectoangles(vec, cam_viewangles);
452 		cam_viewangles[0] = -cam_viewangles[0];
453 	} else {
454 		vectoangles(vec, vec2);
455 		vec2[PITCH] = -vec2[PITCH];
456 
457 		cam_viewangles[PITCH] = adjustang(cam_viewangles[PITCH], vec2[PITCH], cl_camera_maxpitch.value);
458 		cam_viewangles[YAW] = adjustang(cam_viewangles[YAW], vec2[YAW], cl_camera_maxyaw.value);
459 	}
460 	VectorCopy(cam_viewangles, cl.viewangles);
461 	VectorCopy(cl.viewangles, cl.simangles);
462 }
463 #endif
464 
Cam_FinishMove(usercmd_t * cmd)465 void Cam_FinishMove(usercmd_t *cmd)
466 {
467 	int i;
468 	player_info_t	*s;
469 	int end;
470 
471 	if (cls.state != ca_active)
472 		return;
473 
474 	if (!cl.spectator) // only in spectator mode
475 		return;
476 
477 #if 0
478 	if (autocam && locked) {
479 		frame = &cl.frames[cls.netchan.incoming_sequence & UPDATE_MASK];
480 		player = frame->playerstate + spec_track;
481 		self = frame->playerstate + cl.playernum;
482 
483 		VectorSubtract(player->origin, self->origin, vec);
484 		if (cam_forceview) {
485 			cam_forceview = false;
486 			vectoangles(vec, cam_viewangles);
487 			cam_viewangles[0] = -cam_viewangles[0];
488 		} else {
489 			vectoangles(vec, vec2);
490 			vec2[PITCH] = -vec2[PITCH];
491 
492 			cam_viewangles[PITCH] = adjustang(cam_viewangles[PITCH], vec2[PITCH], cl_camera_maxpitch.value);
493 			cam_viewangles[YAW] = adjustang(cam_viewangles[YAW], vec2[YAW], cl_camera_maxyaw.value);
494 		}
495 		VectorCopy(cam_viewangles, cl.viewangles);
496 	}
497 #endif
498 
499 	if (cmd->buttons & BUTTON_ATTACK) {
500 		if (!(oldbuttons & BUTTON_ATTACK)) {
501 
502 			oldbuttons |= BUTTON_ATTACK;
503 			autocam++;
504 
505 			if (autocam > CAM_TRACK) {
506 				Cam_Unlock();
507 				VectorCopy(cl.viewangles, cmd->angles);
508 				return;
509 			}
510 		} else
511 			return;
512 	} else {
513 		oldbuttons &= ~BUTTON_ATTACK;
514 		if (!autocam)
515 			return;
516 	}
517 
518 	if (autocam && cl_hightrack.value) {
519 		Cam_CheckHighTarget();
520 		return;
521 	}
522 
523 	if (locked) {
524 		if ((cmd->buttons & BUTTON_JUMP) && (oldbuttons & BUTTON_JUMP))
525 			return;		// don't pogo stick
526 
527 		if (!(cmd->buttons & BUTTON_JUMP)) {
528 			oldbuttons &= ~BUTTON_JUMP;
529 			return;
530 		}
531 		oldbuttons |= BUTTON_JUMP;	// don't jump again until released
532 	}
533 
534 //	Con_Printf("Selecting track target...\n");
535 
536 	if (locked && autocam)
537 		end = (spec_track + 1) % MAX_CLIENTS;
538 	else
539 		end = spec_track;
540 	i = end;
541 	do {
542 		s = &cl.players[i];
543 		if (s->name[0] && !s->spectator) {
544 			Cam_Lock(i);
545 			return;
546 		}
547 		i = (i + 1) % MAX_CLIENTS;
548 	} while (i != end);
549 	// stay on same guy?
550 	i = spec_track;
551 	s = &cl.players[i];
552 	if (s->name[0] && !s->spectator) {
553 		Cam_Lock(i);
554 		return;
555 	}
556 	Con_Printf("No target found ...\n");
557 	autocam = locked = false;
558 }
559 
Cam_Reset(void)560 void Cam_Reset(void)
561 {
562 	autocam = CAM_NONE;
563 	spec_track = 0;
564 }
565 
CL_InitCam(void)566 void CL_InitCam(void)
567 {
568 	Cvar_RegisterVariable (&cl_hightrack);
569 	Cvar_RegisterVariable (&cl_chasecam);
570 //	Cvar_RegisterVariable (&cl_camera_maxpitch);
571 //	Cvar_RegisterVariable (&cl_camera_maxyaw);
572 }
573 
574 
575