]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/View.qc
Demo camera refactoring
[divverent/nexuiz.git] / data / qcsrc / client / View.qc
1 entity porto;\r
2 vector polyline[16];\r
3 float trace_dphitcontents;\r
4 float Q3SURFACEFLAG_SLICK = 2; // low friction surface\r
5 float DPCONTENTS_PLAYERCLIP = 256; // blocks player movement\r
6 void Porto_Draw()\r
7 {\r
8         vector p, dir, ang, q, nextdir;\r
9         float idx, portal_number, portal1_idx;\r
10 \r
11         if(activeweapon != WEP_PORTO)\r
12                 return;\r
13 \r
14         dir = view_forward;\r
15 \r
16         if(angles_held_status)\r
17         {\r
18                 makevectors(angles_held);\r
19                 dir = v_forward;\r
20         }\r
21 \r
22         p = view_origin;\r
23 \r
24         polyline[0] = p;\r
25         idx = 1;\r
26         portal_number = 0;\r
27         nextdir = dir;\r
28 \r
29         for(;;)\r
30         {\r
31                 dir = nextdir;\r
32                 traceline(p, p + 65536 * dir, TRUE, world);\r
33                 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)\r
34                         return;\r
35                 nextdir = dir - 2 * (dir * trace_plane_normal) * trace_plane_normal; // mirror dir at trace_plane_normal\r
36                 p = trace_endpos;\r
37                 polyline[idx] = p;\r
38                 ++idx;\r
39                 if(idx >= 16)\r
40                         return;\r
41                 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)\r
42                         continue;\r
43                 ++portal_number;\r
44                 ang = vectoangles2(trace_plane_normal, dir);\r
45                 ang_x = -ang_x;\r
46                 makevectors(ang);\r
47                 if(!CheckWireframeBox(porto, p - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward))\r
48                         return;\r
49                 if(portal_number == 1)\r
50                         portal1_idx = idx;\r
51                 if(portal_number >= 2)\r
52                         break;\r
53         }\r
54 \r
55         while(idx >= 2)\r
56         {\r
57                 p = polyline[idx-2];\r
58                 q = polyline[idx-1];\r
59                 if(idx == 2)\r
60                         p = p - view_up * 16;\r
61                 if(idx-1 >= portal1_idx)\r
62                 {\r
63                         Draw_CylindricLine(p, q, 4, "", 1, 0, '0 0 1', 0.5, DRAWFLAG_NORMAL);\r
64                 }\r
65                 else\r
66                 {\r
67                         Draw_CylindricLine(p, q, 4, "", 1, 0, '1 0 0', 0.5, DRAWFLAG_NORMAL);\r
68                 }\r
69                 --idx;\r
70         }\r
71 }\r
72 \r
73 float DPCONTENTS_SOLID = 1; // hit a bmodel, not a bounding box\r
74 float DPCONTENTS_BODY = 32; // hit a bounding box, not a bmodel\r
75 void Porto_Init()\r
76 {\r
77         porto = spawn();\r
78         porto.classname = "porto";\r
79         porto.draw = Porto_Draw;\r
80         porto.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;\r
81 }\r
82 \r
83 float drawtime;\r
84 \r
85 float tan(float x)\r
86\r
87         return sin(x) / cos(x);\r
88 }\r
89 float atan2(float y, float x)\r
90 {\r
91         vector v;\r
92         v = '1 0 0' * x + '0 1 0' * y;\r
93         v = vectoangles(v);\r
94         return v_y * 0.01745329251994329576;\r
95 }\r
96 \r
97 vector GetCurrentFov(float fov)\r
98 {\r
99         float zoomsensitivity, zoomspeed, zoomfactor, zoomdir;\r
100 \r
101         zoomsensitivity = cvar("cl_zoomsensitivity");\r
102         zoomfactor = cvar("cl_zoomfactor");\r
103         if(zoomfactor < 1 || zoomfactor > 16)\r
104                 zoomfactor = 2.5;\r
105         zoomspeed = cvar("cl_zoomspeed");\r
106         if(zoomspeed >= 0)\r
107                 if(zoomspeed < 0.5 || zoomspeed > 16)\r
108                         zoomspeed = 3.5;\r
109 \r
110         zoomdir = button_zoom;\r
111         if(getstati(STAT_ACTIVEWEAPON) == WEP_NEX) // do NOT use switchweapon here\r
112                 zoomdir += button_attack2;\r
113         if(spectatee_status > 0 || isdemo())\r
114         {\r
115                 if(spectatorbutton_zoom)\r
116                         zoomdir = 0 + !zoomdir;\r
117                         // do not even THINK about removing this 0\r
118                         // _I_ know what I am doing\r
119                         // fteqcc does not\r
120         }\r
121 \r
122         if(zoomdir)\r
123                 zoomin_effect = 0;\r
124 \r
125         if(zoomin_effect || camera_active)\r
126         {\r
127                 current_viewzoom = min(1, current_viewzoom + drawframetime);\r
128         }\r
129         else\r
130         {\r
131                 if(zoomspeed < 0) // instant zoom\r
132                 {\r
133                         if(zoomdir)\r
134                                 current_viewzoom = 1 / zoomfactor;\r
135                         else\r
136                                 current_viewzoom = 1;\r
137                 }\r
138                 else\r
139                 {\r
140                         if(zoomdir)\r
141                                 current_viewzoom = 1 / bound(1, 1 / current_viewzoom + drawframetime * zoomspeed * (zoomfactor - 1), zoomfactor);\r
142                         else\r
143                                 current_viewzoom = bound(1 / zoomfactor, current_viewzoom + drawframetime * zoomspeed * (1 - 1 / zoomfactor), 1);\r
144                 }\r
145         }\r
146 \r
147         if(almost_equals(current_viewzoom, 1))\r
148                 current_zoomfraction = 0;\r
149         else if(almost_equals(current_viewzoom, 1/zoomfactor))\r
150                 current_zoomfraction = 1;\r
151         else\r
152                 current_zoomfraction = (current_viewzoom - 1) / (1/zoomfactor - 1);\r
153 \r
154         if(zoomsensitivity < 1)\r
155                 setsensitivityscale(pow(current_viewzoom, 1 - zoomsensitivity));\r
156         else\r
157                 setsensitivityscale(1);\r
158 \r
159         float frustumx, frustumy, fovx, fovy;\r
160         frustumy = tan(fov * 0.00872664625997164788) * 0.75 * current_viewzoom;\r
161         frustumx = frustumy * vid_width / vid_height / vid_pixelheight;\r
162         fovx = atan2(frustumx, 1) / 0.00872664625997164788;\r
163         fovy = atan2(frustumy, 1) / 0.00872664625997164788;\r
164 \r
165         return '1 0 0' * fovx + '0 1 0' * fovy;\r
166 }\r
167 \r
168 void CSQC_common_hud(void);\r
169 \r
170 void CSQC_kh_hud(void);\r
171 void CSQC_ctf_hud(void);\r
172 void PostInit(void);\r
173 void CSQC_Demo_Camera();\r
174 float Sbar_WouldDrawScoreboard ();\r
175 float zoomscript_caught;\r
176 float view_set;\r
177 float camera_mode;\r
178 void CSQC_UpdateView(float w, float h)\r
179 {\r
180         entity e;\r
181         float fov;\r
182         float f;\r
183         vector v1, v2;\r
184 \r
185         dprint_load();\r
186         WaypointSprite_Load();\r
187 \r
188         ticrate = getstatf(STAT_SYS_TICRATE) * getstatf(STAT_MOVEVARS_TIMESCALE);\r
189 \r
190         // Render the Scene\r
191         if(!intermission || !view_set)\r
192         {\r
193                 view_origin = pmove_org + '0 0 1' * getstati(STAT_VIEWHEIGHT);\r
194                 view_angles = input_angles;\r
195                 makevectors(view_angles);\r
196                 view_forward = v_forward;\r
197                 view_right = v_right;\r
198                 view_up = v_up;\r
199                 view_set = 1;\r
200         }\r
201 \r
202         f = floor(cvar("v_flipped"));\r
203         cvar_set("v_flipped", ftos(!f));\r
204         v1 = cs_unproject('-100 -100 1000');\r
205         cvar_set("v_flipped", ftos(f));\r
206         v2 = cs_unproject('-100 -100 1000');\r
207 \r
208         if(v1 == v2)\r
209         {\r
210                 // non-supporting engine\r
211                 vid_width = cvar("vid_width");\r
212                 vid_height = cvar("vid_height");\r
213         }\r
214         else\r
215         {\r
216                 // supporting engine\r
217                 vid_width = w;\r
218                 vid_height = h;\r
219         }\r
220 \r
221 #ifdef BLURTEST\r
222         if(time > blurtest_time0 && time < blurtest_time1)\r
223         {\r
224                 float r, t;\r
225 \r
226                 t = (time - blurtest_time0) / (blurtest_time1 - blurtest_time0);\r
227                 r = t * blurtest_radius;\r
228                 f = 1 / pow(t, blurtest_power) - 1;\r
229 \r
230                 cvar_set("r_glsl_postprocess", "1");\r
231                 cvar_set("r_glsl_postprocess_uservec1", strcat(ftos(r), " ", ftos(f), " 0 0"));\r
232         }\r
233         else\r
234         {\r
235                 cvar_set("r_glsl_postprocess", "0");\r
236                 cvar_set("r_glsl_postprocess_uservec1", "0 0 0 0");\r
237         }\r
238 #endif\r
239 \r
240         Fog_Force();\r
241 \r
242         drawframetime = max(0.000001, time - drawtime);\r
243         drawtime = time;\r
244 \r
245         // watch for gametype changes here...\r
246         // in ParseStuffCMD the cmd isn't executed yet :/\r
247         // might even be better to add the gametype to TE_CSQC_INIT...?\r
248         if(!postinit)\r
249                 PostInit();\r
250 \r
251         fov = cvar("fov");\r
252         if(button_zoom || fov <= 59.5)\r
253         {\r
254                 if(!zoomscript_caught)\r
255                 {\r
256                         localcmd("+button4\n");\r
257                         zoomscript_caught = 1;\r
258                         ignore_plus_zoom += 1;\r
259                 }\r
260         }\r
261         else\r
262         {\r
263                 if(zoomscript_caught)\r
264                 {\r
265                         localcmd("-button4\n");\r
266                         zoomscript_caught = 0;\r
267                         ignore_minus_zoom += 1;\r
268                 }\r
269         }\r
270         \r
271         sbar_alpha_fg = cvar("sbar_alpha_fg" );\r
272         sbar_hudselector = cvar("sbar_hudselector");\r
273         ColorTranslateMode = cvar("cl_stripcolorcodes");\r
274         activeweapon = getstati(STAT_SWITCHWEAPON);\r
275         f = cvar("teamplay");\r
276         if(f != teamplay)\r
277         {\r
278                 teamplay = f;\r
279                 Sbar_InitScores();\r
280         }\r
281 \r
282         if(last_weapon != activeweapon) {\r
283                 weapontime = time;\r
284                 last_weapon = activeweapon;\r
285         }\r
286 \r
287         // ALWAYS Clear Current Scene First\r
288         R_ClearScene();\r
289 \r
290         // Assign Standard Viewflags\r
291         // Draw the World (and sky)\r
292         R_SetView(VF_DRAWWORLD, 1);\r
293 \r
294         R_SetView(VF_FOV, GetCurrentFov(fov));\r
295 \r
296         // Camera for demo playback\r
297         if(camera_active)\r
298         {\r
299                 if(cvar("camera_enable"))\r
300                         CSQC_Demo_Camera();\r
301                 else\r
302                 {\r
303                         cvar_set("chase_active", ftos(chase_active_backup));\r
304                         cvar_set("cl_demo_mousegrab", "0");\r
305                         camera_active = FALSE;\r
306                 }\r
307         }\r
308 #ifdef CAMERATEST\r
309         else if(cvar("camera_enable"))\r
310 #else\r
311         else if(cvar("camera_enable") && isdemo())\r
312 #endif\r
313         {\r
314                 // Enable required Darkplaces cvars\r
315                 chase_active_backup = cvar("chase_active");\r
316                 cvar_set("chase_active", "2");          \r
317                 cvar_set("cl_demo_mousegrab", "1");\r
318                 camera_active = TRUE;\r
319                 camera_mode = FALSE;\r
320         }\r
321         \r
322         // Draw the Crosshair\r
323         float scoreboard_active;\r
324         scoreboard_active = Sbar_WouldDrawScoreboard();\r
325         R_SetView(VF_DRAWCROSSHAIR, 0); //Make sure engine crosshairs are always hidden\r
326         \r
327         // Draw the Engine Status Bar (the default Quake HUD)\r
328         R_SetView(VF_DRAWENGINESBAR, 0);\r
329 \r
330         // Set the console size vars\r
331         vid_conwidth = cvar("vid_conwidth");\r
332         vid_conheight = cvar("vid_conheight");\r
333         vid_pixelheight = cvar("vid_pixelheight");\r
334 \r
335         // fetch this one only once per frame\r
336         sbar_showbinds = cvar("sbar_showbinds");\r
337         sbar_showbinds_limit = cvar("sbar_showbinds_limit");\r
338 \r
339         // Update the mouse position\r
340         /*\r
341         mousepos_x = vid_conwidth;\r
342         mousepos_y = vid_conheight;\r
343         mousepos = mousepos*0.5 + getmousepos();\r
344         */\r
345 \r
346         R_AddEntities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS);\r
347 \r
348         e = self;\r
349         for(self = world; (self = nextent(self)); )\r
350                 if(self.draw)\r
351                         self.draw();\r
352         self = e;\r
353         R_RenderScene();\r
354 \r
355         // now switch to 2D drawing mode by calling a 2D drawing function\r
356         // then polygon drawing will draw as 2D stuff, and NOT get queued until the\r
357         // next R_RenderScene call\r
358         drawstring('0 0 0', "", '1 1 0', '1 1 1', 0, 0);\r
359 \r
360         // crosshair\r
361         if(!scoreboard_active && !ons_showmap) {\r
362                 string wcross_style;\r
363                 wcross_style = cvar_string("crosshair");\r
364 \r
365                 if (wcross_style != "0") {\r
366                         vector wcross_color, wcross_size;\r
367                         string wcross_wep, wcross_name;\r
368                         float wcross_alpha, wcross_sizefloat;\r
369 \r
370                         wcross_color_x = cvar("crosshair_color_red");\r
371                         wcross_color_y = cvar("crosshair_color_green");\r
372                         wcross_color_z = cvar("crosshair_color_blue");\r
373                         wcross_alpha = cvar("crosshair_color_alpha");\r
374                         wcross_sizefloat = cvar("crosshair_size");\r
375                         if (cvar("crosshair_per_weapon")) {\r
376                                 e = get_weaponinfo(activeweapon);\r
377                                 if (e && e.netname != "")\r
378                                 {\r
379                                         wcross_wep = e.netname;\r
380                                         wcross_style = cvar_string(strcat("crosshair_", wcross_wep));\r
381                                         if(wcross_style == "")\r
382                                                 wcross_style = e.netname;\r
383 \r
384                                         if(!cvar("crosshair_color_override"))\r
385                                         {\r
386                                                 wcross_color_x = cvar(strcat("crosshair_", wcross_wep, "_color_red"));\r
387                                                 wcross_color_y = cvar(strcat("crosshair_", wcross_wep, "_color_green"));\r
388                                                 wcross_color_z = cvar(strcat("crosshair_", wcross_wep, "_color_blue"));\r
389                                         }\r
390 \r
391                                         wcross_alpha *= cvar(strcat("crosshair_", wcross_wep, "_color_alpha"));\r
392                                         wcross_sizefloat *= cvar(strcat("crosshair_", wcross_wep, "_size"));\r
393                                 }\r
394                         }\r
395 \r
396                         wcross_name = strcat("gfx/crosshair", wcross_style);\r
397                 \r
398                         wcross_size = drawgetimagesize(wcross_name);\r
399                         wcross_size_x *= wcross_sizefloat;\r
400                         wcross_size_y *= wcross_sizefloat;\r
401 \r
402                         drawpic('0.5 0 0' * (vid_conwidth - wcross_size_x) + '0 0.5 0' * (vid_conheight - wcross_size_y), wcross_name, wcross_size, wcross_color, wcross_alpha, DRAWFLAG_NORMAL);\r
403                 }\r
404         }\r
405 \r
406         // Draw the mouse cursor\r
407         // NOTE: drawpic must happen after R_RenderScene for some reason\r
408         //drawpic(getmousepos(), "gfx/cursor.tga", '11 14 0', '1 1 1', 1, 0);\r
409         //drawstring('50 50', ftos(game), '10 10 0', '1 1 1', 1, 0);\r
410         //self = edict_num(player_localnum);\r
411         //drawstring('0 0', vtos(pmove_org), '8 8 0', '1 1 1', 1, 0);\r
412         //drawstring('0 8', strcat("ORG: ", vtos(self.origin), " state: ", ftos(self.ctf_state), " HP: ", ftos(self.health)), '8 8 0', '1 1 1', 1, 0);\r
413         // as long as the ctf part isn't in, this is useless\r
414         if(menu_visible)\r
415                 menu_show();\r
416         \r
417         /*if(gametype == GAME_CTF)\r
418         {\r
419                 ctf_view();\r
420         } else */\r
421 \r
422         // draw 2D entities\r
423         e = self;\r
424         for(self = world; (self = nextent(self)); )\r
425                 if(self.draw2d)\r
426                         self.draw2d();\r
427         self = e;\r
428         \r
429         // draw radar\r
430         if(teamplay || cvar("cl_teamradar") == 2)\r
431         {\r
432                 if((cvar_string("cl_teamradar") != "0" && !scoreboard_active) || ons_showmap)\r
433                         teamradar_view();\r
434         }\r
435 \r
436         // draw sbar\r
437         if(cvar("r_letterbox") == 0)\r
438         if(cvar("viewsize") < 120)\r
439                 CSQC_common_hud();\r
440 }\r
441 \r
442 void Sbar_Draw();\r
443 void CSQC_common_hud(void)\r
444 {\r
445         // Sbar_SortFrags(); done in Sbar_Draw\r
446         Sbar_Draw();\r
447 }\r
448 \r
449 // KeyHunt HUD by victim\r
450 void CSQC_kh_hud(void)\r
451 {\r
452         // HUD 0 has the weapons on the right hand side - temporarily shown when needed\r
453         // HUD 1 has the weapons on the bottom - permanently\r
454 \r
455         // use the following two binds to check the icons move correctly\r
456         // bind g "toggle sbar_flagstatus_right; echo Menu right $sbar_flagstatus_right"  // move the icons\r
457         // bind h "toggle sbar_hudselector; echo HUD $sbar_hudselector"  // change the HUD\r
458 \r
459         float kh_keys, kh_keys_status, kh_teams_set;\r
460         float kh_margin_x, kh_margin_y, kh_key_box;\r
461         string kh_carrying, kh_outline;\r
462         vector red_pos, blue_pos, yellow_pos, pink_pos, kh_size;\r
463         vector red, blue, yellow, pink;\r
464 \r
465         kh_keys = getstati(STAT_KH_KEYS);\r
466         kh_keys_status = kh_keys / 256;\r
467         kh_teams_set = cvar("_teams_available");  // set in keyhunt.qc\r
468 \r
469         kh_margin_y = 8;\r
470         kh_margin_x = (cvar("sbar_flagstatus_right") * sbar_hudselector * (vid_conwidth - 67)) + 10;\r
471 //      sbar_flagstatus_right 0/1; sbar_hudselector 0/1; screen width - key width + margin\r
472 \r
473         red_pos_x = blue_pos_x = yellow_pos_x = pink_pos_x = kh_margin_x;\r
474 \r
475         kh_key_box = 120;\r
476 \r
477         pink_pos_y = kh_margin_y + 0;  // top\r
478         yellow_pos_y = kh_margin_y + kh_key_box;\r
479         blue_pos_y = kh_margin_y + kh_key_box * 2;\r
480         red_pos_y = kh_margin_y + kh_key_box * 3;  //bottom\r
481 \r
482         red = '1 0 0';\r
483         blue = '0 0 1';\r
484         yellow = '1 1 0';\r
485         pink = '1 0 1';\r
486 \r
487         kh_size = '0 0 0';  // don't resize the image\r
488 \r
489         kh_carrying = "gfx/sb_kh_full";\r
490         kh_outline = "gfx/sb_kh_outline";\r
491 \r
492 //      drawpic(vector position, string pic, vector size, vector rgb, float alpha, float flag)\r
493 //      vector position = '0 0';  // 'x y' 0 0 (the origin) is the top left. X 0 - 799, Y 0 - 599\r
494 \r
495 //      vector size = '0 0';  // 'x y' changes the x & y dimensions. '0 0' gives the default pic size\r
496 //      vector rgb = '0 0 0';  // 'r g b' range 0 - 1\r
497 \r
498         if (kh_keys_status & 1)  // red\r
499                 drawpic (red_pos, kh_carrying, kh_size, red, 0.2, 0);  // show 20% alpha key\r
500         else\r
501                 drawpic (red_pos, kh_outline, kh_size, red, 0.4, 0);  // show key outline 40% alpha\r
502 \r
503         if (kh_keys & 1)\r
504                 drawpic (red_pos, kh_carrying, kh_size, red, 1.0, 0);  // show solid key 100% alpha\r
505 \r
506 \r
507         if (kh_keys_status & 2)  // blue\r
508                 drawpic (blue_pos, kh_carrying, kh_size, blue, 0.2, 0);\r
509         else\r
510                 drawpic (blue_pos, kh_outline, kh_size, blue, 0.4, 0);\r
511 \r
512         if (kh_keys & 2)\r
513                 drawpic (blue_pos, kh_carrying, kh_size, blue, 1.0, 0);\r
514 \r
515 \r
516         if (kh_teams_set & 4)  // yellow\r
517         {\r
518                 if (kh_keys_status & 4)\r
519                         drawpic (yellow_pos, kh_carrying, kh_size, yellow, 0.2, 0);\r
520                 else\r
521                         drawpic (yellow_pos, kh_outline, kh_size, yellow, 0.4, 0);\r
522 \r
523                 if (kh_keys & 4)\r
524                         drawpic (yellow_pos, kh_carrying, kh_size, yellow, 1.0, 0);\r
525         }\r
526 \r
527 \r
528         if (kh_teams_set & 8)  // pink\r
529         {\r
530                 if (kh_keys_status & 8)\r
531                         drawpic (pink_pos, kh_carrying, kh_size, pink, 0.2, 0);\r
532                 else\r
533                         drawpic (pink_pos, kh_outline, kh_size, pink, 0.4, 0);\r
534 \r
535                 if (kh_keys & 8)\r
536                         drawpic (pink_pos, kh_carrying, kh_size, pink, 1.0, 0);\r
537         }\r
538 \r
539 }\r
540 \r
541 // following vectors must be global to allow seamless switching between camera modes\r
542 vector camera_offset, current_camera_offset, mouse_angles, current_angles, current_origin, camera_position, current_position;\r
543 void CSQC_Demo_Camera()\r
544 {\r
545         float speed, attenuation, dimensions;\r
546         vector tmp, delta;\r
547 \r
548         if( cvar("camera_reset") || !camera_mode )\r
549         {\r
550                 camera_offset = '0 0 0';\r
551                 current_angles = '0 0 0';\r
552                 camera_direction = '0 0 0';\r
553                 camera_offset_z += 30;\r
554                 camera_offset_x += 30 * -cos(current_angles_y * DEG2RAD);\r
555                 camera_offset_y += 30 * -sin(current_angles_y * DEG2RAD);\r
556                 current_origin = view_origin;\r
557                 current_camera_offset  = camera_offset;\r
558                 cvar_set("camera_reset", "0");\r
559                 camera_mode = CAMERA_CHASE;\r
560         }\r
561 \r
562         // Camera angles\r
563         if( cvar("camera_roll") )\r
564                 mouse_angles_z += cvar("camera_roll") * cvar("camera_speed_roll");\r
565 \r
566         if(cvar("camera_look_player"))\r
567         {\r
568                 local vector dir;\r
569                 local float n;\r
570 \r
571                 dir = normalize(view_origin - current_position);\r
572                 n = mouse_angles_z;\r
573                 mouse_angles = vectoangles(dir);\r
574                 mouse_angles_x = mouse_angles_x * -1;\r
575                 mouse_angles_z = n;\r
576         }\r
577         else\r
578         {\r
579                 tmp = getmousepos() * 0.1;\r
580                 if(vlen(tmp)>cvar("camera_mouse_treshold"))\r
581                 {\r
582                         mouse_angles_x += tmp_y * cos(mouse_angles_z * DEG2RAD) + (tmp_x * sin(mouse_angles_z * DEG2RAD));\r
583                         mouse_angles_y -= tmp_x * cos(mouse_angles_z * DEG2RAD) + (tmp_y * -sin(mouse_angles_z * DEG2RAD));\r
584                 }\r
585         }\r
586 \r
587         while (mouse_angles_x < -180) mouse_angles_x = mouse_angles_x + 360;\r
588         while (mouse_angles_x > 180) mouse_angles_x = mouse_angles_x - 360;\r
589         while (mouse_angles_y < -180) mouse_angles_y = mouse_angles_y + 360;\r
590         while (mouse_angles_y > 180) mouse_angles_y = mouse_angles_y - 360;\r
591 \r
592         // Wrap angles for quick movements\r
593         delta = '0 0 0';\r
594         if(mouse_angles_y < -60 && current_angles_y > 60)\r
595                 delta = '0 360 0';\r
596         if(mouse_angles_y > 60 && current_angles_y < -60)\r
597                 delta = '0 -360 0';\r
598         //\r
599 \r
600         if(cvar("camera_look_player"))\r
601                 attenuation = cvar("camera_look_attenuation");\r
602         else\r
603                 attenuation = cvar("camera_speed_attenuation");\r
604 \r
605         attenuation = 1 / max(1, attenuation);\r
606         current_angles += (mouse_angles - current_angles + delta) * attenuation;\r
607 \r
608         while (current_angles_x < -180) current_angles_x = current_angles_x + 360;\r
609         while (current_angles_x > 180) current_angles_x = current_angles_x - 360;\r
610         while (current_angles_y < -180) current_angles_y = current_angles_y + 360;\r
611         while (current_angles_y > 180) current_angles_y = current_angles_y - 360;\r
612 \r
613         // Camera position\r
614         tmp = '0 0 0';\r
615         dimensions = 0;\r
616 \r
617         if( camera_direction_x )\r
618         {\r
619                 tmp_x = camera_direction_x * cos(current_angles_y * DEG2RAD);\r
620                 tmp_y = camera_direction_x * sin(current_angles_y * DEG2RAD);\r
621                 if( cvar("camera_forward_follows") && !cvar("camera_look_player") )\r
622                         tmp_z = camera_direction_x * -sin(current_angles_x * DEG2RAD);\r
623                 ++dimensions;\r
624         }\r
625 \r
626         if( camera_direction_y )\r
627         {\r
628                 tmp_x += camera_direction_y * -sin(current_angles_y * DEG2RAD);\r
629                 tmp_y += camera_direction_y * cos(current_angles_y * DEG2RAD) * cos(current_angles_z * DEG2RAD);\r
630                 tmp_z += camera_direction_y * sin(current_angles_z * DEG2RAD);\r
631                 ++dimensions;\r
632         }\r
633 \r
634         if( camera_direction_z )\r
635         {\r
636                 tmp_z += camera_direction_z * cos(current_angles_z * DEG2RAD);\r
637                 ++dimensions;\r
638         }\r
639 \r
640         if(cvar("camera_free"))\r
641                 speed = cvar("camera_speed_free");\r
642         else\r
643                 speed = cvar("camera_speed_chase");\r
644 \r
645         if(dimensions)\r
646         {\r
647                 speed = speed * sqrt(1 / dimensions);\r
648                 camera_offset += tmp * speed;\r
649         }\r
650 \r
651         current_camera_offset += (camera_offset - current_camera_offset) * attenuation;\r
652 \r
653         // Camera modes\r
654         if( cvar("camera_free") )\r
655         {\r
656                 if ( camera_mode == CAMERA_CHASE )\r
657                 {\r
658                         current_camera_offset = current_origin + current_camera_offset;\r
659                         camera_offset = current_origin + camera_offset;\r
660                 }\r
661 \r
662                 camera_mode = CAMERA_FREE;\r
663                 current_position = current_camera_offset;\r
664         }\r
665         else\r
666         {\r
667                 if ( camera_mode == CAMERA_FREE )\r
668                 {\r
669                         current_origin = view_origin;\r
670                         camera_offset = camera_offset - current_origin;\r
671                         current_camera_offset = current_camera_offset - current_origin;\r
672                 }\r
673 \r
674                 camera_mode = CAMERA_CHASE;\r
675 \r
676                 if(cvar("camera_chase_smoothly"))\r
677                         current_origin += (view_origin - current_origin) * attenuation;\r
678                 else\r
679                         current_origin = view_origin;\r
680 \r
681                 current_position = current_origin + current_camera_offset;\r
682         }\r
683 \r
684         R_SetView(VF_ANGLES, current_angles);\r
685         R_SetView(VF_ORIGIN, current_position);\r
686 }\r