]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/View.qc
4ea84c639269ee724ac7049555307969411c5863
[divverent/nexuiz.git] / data / qcsrc / client / View.qc
1 #define spider_rocket_icon "gfx/spiderbot/rocket_ico.tga"
2 #define spider_rocket_targ "gfx/spiderbot/target.tga"
3 #define SPIDER_CROSS "textures/spiderbot/cross.tga"
4 #define rkt_size 32
5 #define rld_size_x 256
6 #define rld_size_y 16
7
8 void CSQC_WAKIZASHI_HUD();
9
10 entity porto;
11 vector polyline[16];
12 float trace_dphitcontents;
13 float trace_networkentity;
14 float Q3SURFACEFLAG_SLICK = 2; // low friction surface
15 float DPCONTENTS_SOLID = 1; // blocks player movement
16 float DPCONTENTS_BODY = 32; // blocks player movement
17 float DPCONTENTS_CORPSE = 64; // blocks player movement
18 float DPCONTENTS_PLAYERCLIP = 256; // blocks player movement
19 void Porto_Draw()
20 {
21         vector p, dir, ang, q, nextdir;
22         float idx, portal_number, portal1_idx;
23
24         if(activeweapon != WEP_PORTO || spectatee_status || gametype == GAME_NEXBALL)
25                 return;
26         if(intermission == 1)
27                 return;
28         if(intermission == 2)
29                 return;
30         if (getstati(STAT_HEALTH) <= 0)
31                 return;
32
33         dir = view_forward;
34
35         if(angles_held_status)
36         {
37                 makevectors(angles_held);
38                 dir = v_forward;
39         }
40
41         p = view_origin;
42
43         polyline[0] = p;
44         idx = 1;
45         portal_number = 0;
46         nextdir = dir;
47
48         for(;;)
49         {
50                 dir = nextdir;
51                 traceline(p, p + 65536 * dir, TRUE, porto);
52                 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
53                         return;
54                 nextdir = dir - 2 * (dir * trace_plane_normal) * trace_plane_normal; // mirror dir at trace_plane_normal
55                 p = trace_endpos;
56                 polyline[idx] = p;
57                 ++idx;
58                 if(idx >= 16)
59                         return;
60                 if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP)
61                         continue;
62                 ++portal_number;
63                 ang = vectoangles2(trace_plane_normal, dir);
64                 ang_x = -ang_x;
65                 makevectors(ang);
66                 if(!CheckWireframeBox(porto, p - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward))
67                         return;
68                 if(portal_number == 1)
69                         portal1_idx = idx;
70                 if(portal_number >= 2)
71                         break;
72         }
73
74         while(idx >= 2)
75         {
76                 p = polyline[idx-2];
77                 q = polyline[idx-1];
78                 if(idx == 2)
79                         p = p - view_up * 16;
80                 if(idx-1 >= portal1_idx)
81                 {
82                         Draw_CylindricLine(p, q, 4, "", 1, 0, '0 0 1', 0.5, DRAWFLAG_NORMAL);
83                 }
84                 else
85                 {
86                         Draw_CylindricLine(p, q, 4, "", 1, 0, '1 0 0', 0.5, DRAWFLAG_NORMAL);
87                 }
88                 --idx;
89         }
90 }
91
92 /**
93  * Checks whether the server initiated a map restart (stat_game_starttime changed)
94  *
95  * TODO: Use a better solution where a common shared entitiy is used that contains
96  * timelimit, fraglimit and game_starttime! Requires engine changes (remove STAT_TIMELIMIT
97  * and STAT_FRAGLIMIT to be auto-sent)
98  */
99 void CheckForGamestartChange() {
100         float startTime;
101         startTime = getstatf(STAT_GAMESTARTTIME);
102         if (previous_game_starttime != startTime) {
103                 if ((time + 5.0) < startTime) {
104                         //if connecting to server while restart was active don't always play prepareforbattle
105                         sound(self, CHAN_VOICE, "announcer/robotic/prepareforbattle.wav", VOL_BASEVOICE, ATTN_NONE);
106                 }
107                 if (time < startTime) {
108                         restartAnnouncer = spawn();
109                         restartAnnouncer.think = restartAnnouncer_Think;
110                         restartAnnouncer.nextthink = startTime - floor(startTime - time); //synchronize nextthink to startTime
111                 }
112         }
113         previous_game_starttime = startTime;
114 }
115
116 void Porto_Init()
117 {
118         porto = spawn();
119         porto.classname = "porto";
120         porto.draw = Porto_Draw;
121         porto.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP;
122 }
123
124 float drawtime;
125
126 float tan(float x)
127 {
128         return sin(x) / cos(x);
129 }
130 float atan2(float y, float x)
131 {
132         vector v;
133         v = '1 0 0' * x + '0 1 0' * y;
134         v = vectoangles(v);
135         return v_y * 0.01745329251994329576;
136 }
137
138 vector GetCurrentFov(float fov)
139 {
140         float zoomsensitivity, zoomspeed, zoomfactor, zoomdir;
141
142         zoomsensitivity = cvar("cl_zoomsensitivity");
143         zoomfactor = cvar("cl_zoomfactor");
144         if(zoomfactor < 1 || zoomfactor > 16)
145                 zoomfactor = 2.5;
146         zoomspeed = cvar("cl_zoomspeed");
147         if(zoomspeed >= 0)
148                 if(zoomspeed < 0.5 || zoomspeed > 16)
149                         zoomspeed = 3.5;
150
151         zoomdir = button_zoom;
152         if(getstati(STAT_ACTIVEWEAPON) == WEP_NEX) // do NOT use switchweapon here
153                 zoomdir += button_attack2;
154         if(spectatee_status > 0 || isdemo())
155         {
156                 if(spectatorbutton_zoom)
157                         zoomdir = 0 + !zoomdir;
158                 // do not even THINK about removing this 0
159                 // _I_ know what I am doing
160                 // fteqcc does not
161         }
162
163         if(zoomdir)
164                 zoomin_effect = 0;
165
166         if(zoomin_effect || camera_active)
167         {
168                 current_viewzoom = min(1, current_viewzoom + drawframetime);
169         }
170         else
171         {
172                 if(zoomspeed < 0) // instant zoom
173                 {
174                         if(zoomdir)
175                                 current_viewzoom = 1 / zoomfactor;
176                         else
177                                 current_viewzoom = 1;
178                 }
179                 else
180                 {
181                         if(zoomdir)
182                                 current_viewzoom = 1 / bound(1, 1 / current_viewzoom + drawframetime * zoomspeed * (zoomfactor - 1), zoomfactor);
183                         else
184                                 current_viewzoom = bound(1 / zoomfactor, current_viewzoom + drawframetime * zoomspeed * (1 - 1 / zoomfactor), 1);
185                 }
186         }
187
188         if(almost_equals(current_viewzoom, 1))
189                 current_zoomfraction = 0;
190         else if(almost_equals(current_viewzoom, 1/zoomfactor))
191                 current_zoomfraction = 1;
192         else
193                 current_zoomfraction = (current_viewzoom - 1) / (1/zoomfactor - 1);
194
195         if(zoomsensitivity < 1)
196                 setsensitivityscale(pow(current_viewzoom, 1 - zoomsensitivity));
197         else
198                 setsensitivityscale(1);
199
200         float frustumx, frustumy, fovx, fovy;
201         frustumy = tan(fov * 0.00872664625997164788) * 0.75 * current_viewzoom;
202         frustumx = frustumy * vid_width / vid_height / vid_pixelheight;
203         fovx = atan2(frustumx, 1) / 0.00872664625997164788;
204         fovy = atan2(frustumy, 1) / 0.00872664625997164788;
205
206         return '1 0 0' * fovx + '0 1 0' * fovy;
207 }
208
209 // this function must match W_SetupShot!
210 float zoomscript_caught;
211
212 vector wcross_origin;
213 float wcross_scale_prev, wcross_alpha_prev;
214 vector wcross_color_prev;
215 float wcross_scale_goal_prev, wcross_alpha_goal_prev;
216 vector wcross_color_goal_prev;
217 float wcross_changedonetime;
218
219 string wcross_name_goal_prev, wcross_name_goal_prev_prev;
220 float wcross_resolution_goal_prev, wcross_resolution_goal_prev_prev;
221 float wcross_name_changestarttime, wcross_name_changedonetime;
222 float wcross_name_alpha_goal_prev, wcross_name_alpha_goal_prev_prev;
223 entity trueaim;
224 entity trueaim_rifle;
225
226 #define SHOTTYPE_HITTEAM 1
227 #define SHOTTYPE_HITOBSTRUCTION 2
228 #define SHOTTYPE_HITWORLD 3
229 #define SHOTTYPE_HITENEMY 4
230
231 void TrueAim_Init()
232 {
233         trueaim = spawn();
234         trueaim.classname = "trueaim";
235         trueaim.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
236         trueaim_rifle = spawn();
237         trueaim_rifle.classname = "trueaim_rifle";
238         trueaim_rifle.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE;
239 }
240
241 float EnemyHitCheck()
242 {
243         float t;
244         wcross_origin = project_3d_to_2d(trace_endpos);
245         wcross_origin_z = 0;
246         if(trace_networkentity < 1)
247                 return SHOTTYPE_HITWORLD;
248         if(trace_networkentity > maxclients)
249                 return SHOTTYPE_HITWORLD;
250         t = GetPlayerColor(trace_networkentity - 1);
251         if(teamplay)
252                 if(t == myteam)
253                         return SHOTTYPE_HITTEAM;
254         if(t == COLOR_SPECTATOR)
255                 return SHOTTYPE_HITWORLD;
256         return SHOTTYPE_HITENEMY;
257 }
258
259 float TrueAimCheck()
260 {
261         float nudge = 1; // added to traceline target and subtracted from result
262         vector vecs, trueaimpoint, w_shotorg;
263         vector mi, ma, dv;
264         float shottype;
265         entity ta;
266         float mv;
267
268         mi = ma = '0 0 0';
269         ta = trueaim;
270         mv = MOVE_NOMONSTERS;
271
272         switch(activeweapon)
273         {
274                 case WEP_TUBA: // no aim
275                 case WEP_PORTO: // shoots from eye
276                 case WEP_HOOK: // no trueaim
277                 case WEP_GRENADE_LAUNCHER: // toss curve
278                         return SHOTTYPE_HITWORLD;
279                 case WEP_NEX:
280                 case WEP_MINSTANEX:
281                         mv = MOVE_NORMAL;
282                         break;
283                 case WEP_CAMPINGRIFLE:
284                         ta = trueaim_rifle;
285                         mv = MOVE_NORMAL;
286                         if(zoomscript_caught)
287                         {
288                                 tracebox(view_origin, '0 0 0', '0 0 0', view_origin + view_forward * MAX_SHOT_DISTANCE, mv, ta);
289                                 return EnemyHitCheck();
290                         }
291                         break;
292                 case WEP_ROCKET_LAUNCHER: // projectile has a size!
293                         mi = '-3 -3 -3';
294                         ma = '3 3 3';
295                         break;
296                 case WEP_FIREBALL: // projectile has a size!
297                         mi = '-16 -16 -16';
298                         ma = '16 16 16';
299                         break;
300                 case WEP_ELECTRO: // projectile has a size!
301                         mi = '0 0 -3';
302                         ma = '0 0 -3';
303                         break;
304         }
305
306         vecs = decompressShotOrigin(getstati(STAT_SHOTORG));
307
308         traceline(view_origin, view_origin + view_forward * MAX_SHOT_DISTANCE, mv, ta);
309         trueaimpoint = trace_endpos;
310
311         if(vecs_x > 0)
312                 vecs_y = -vecs_y;
313         else
314                 vecs = '0 0 0';
315
316         dv = view_right * vecs_y + view_up * vecs_z;
317         w_shotorg = view_origin + dv;
318
319         // now move the vecs forward as much as requested if possible
320         tracebox(w_shotorg, mi, ma, w_shotorg + view_forward * (vecs_x + nudge), MOVE_NORMAL, ta); // FIXME this MOVE_NORMAL part will misbehave a little in csqc
321         w_shotorg = trace_endpos - view_forward * nudge;
322
323         tracebox(w_shotorg, mi, ma, trueaimpoint, MOVE_NORMAL, ta);
324         shottype = EnemyHitCheck();
325         if(shottype != SHOTTYPE_HITWORLD)
326                 return shottype;
327
328 #if 0
329         // FIXME WHY DOES THIS NOT WORK FOR THE ROCKET LAUNCHER?
330         // or rather, I know why, but see no fix
331         if(vlen(trace_endpos - trueaimpoint) > vlen(ma) + vlen(mi) + 1)
332                 // yes, this is an ugly hack... but it seems good enough to find out whether the test hits the same place as the initial trace
333                 return SHOTTYPE_HITOBSTRUCTION;
334 #endif
335
336         return SHOTTYPE_HITWORLD;
337 }
338
339 void CSQC_common_hud(void);
340
341 void CSQC_kh_hud(void);
342 void CSQC_ctf_hud(void);
343 void PostInit(void);
344 void CSQC_Demo_Camera();
345 float Sbar_WouldDrawScoreboard ();
346 float view_set;
347 float camera_mode;
348 string NextFrameCommand;
349 void CSQC_spider_HUD();
350 void CSQC_UpdateView(float w, float h)
351 {
352         entity e;
353         float fov;
354         float f, i, j;
355         vector v;
356
357         dprint_load();
358         WaypointSprite_Load();
359
360         if(spectatee_status)
361                 myteam = GetPlayerColor(spectatee_status - 1);
362         else
363                 myteam = GetPlayerColor(player_localentnum - 1);
364
365         ticrate = getstatf(STAT_MOVEVARS_TICRATE) * getstatf(STAT_MOVEVARS_TIMESCALE);
366
367         // Render the Scene
368         if(!intermission || !view_set)
369         {
370                 view_origin = pmove_org + '0 0 1' * getstati(STAT_VIEWHEIGHT);
371                 view_angles = input_angles;
372                 makevectors(view_angles);
373                 view_forward = v_forward;
374                 view_right = v_right;
375                 view_up = v_up;
376                 view_set = 1;
377         }
378
379         vid_width = w;
380         vid_height = h;
381
382 #ifdef BLURTEST
383         if(time > blurtest_time0 && time < blurtest_time1)
384         {
385                 float r, t;
386
387                 t = (time - blurtest_time0) / (blurtest_time1 - blurtest_time0);
388                 r = t * blurtest_radius;
389                 f = 1 / pow(t, blurtest_power) - 1;
390
391                 cvar_set("r_glsl_postprocess", "1");
392                 cvar_set("r_glsl_postprocess_uservec1", strcat(ftos(r), " ", ftos(f), " 0 0"));
393         }
394         else
395         {
396                 cvar_set("r_glsl_postprocess", "0");
397                 cvar_set("r_glsl_postprocess_uservec1", "0 0 0 0");
398         }
399 #endif
400
401         Fog_Force();
402
403         drawframetime = max(0.000001, time - drawtime);
404         drawtime = time;
405
406         // watch for gametype changes here...
407         // in ParseStuffCMD the cmd isn't executed yet :/
408         // might even be better to add the gametype to TE_CSQC_INIT...?
409         if(!postinit)
410                 PostInit();
411
412         if(intermission && !isdemo() && !(calledhooks & HOOK_END))
413                 if(calledhooks & HOOK_START)
414                 {
415                         localcmd("\ncl_hook_gameend;");
416                         calledhooks |= HOOK_END;
417                 }
418
419         CheckForGamestartChange();
420         maptimeAnnouncer();
421
422         fov = cvar("fov");
423         if(button_zoom || fov <= 59.5)
424         {
425                 if(!zoomscript_caught)
426                 {
427                         localcmd("+button4\n");
428                         zoomscript_caught = 1;
429                         ignore_plus_zoom += 1;
430                 }
431         }
432         else
433         {
434                 if(zoomscript_caught)
435                 {
436                         localcmd("-button4\n");
437                         zoomscript_caught = 0;
438                         ignore_minus_zoom += 1;
439                 }
440         }
441
442         sbar_alpha_fg = cvar("sbar_alpha_fg" ) * (1 - cvar("_menu_alpha"));
443         sbar_currentammo = cvar("sbar_showcurrentammo");
444         sbar_hudselector = cvar("sbar_hudselector");
445         sbar_hud_accuracy = cvar("sbar_hud_accuracy");
446         ColorTranslateMode = cvar("cl_stripcolorcodes");
447         activeweapon = getstati(STAT_SWITCHWEAPON);
448         f = cvar("teamplay");
449         if(f != teamplay)
450         {
451                 teamplay = f;
452                 Sbar_InitScores();
453         }
454
455         if(last_weapon != activeweapon) {
456                 weapontime = time;
457                 last_weapon = activeweapon;
458         }
459
460         // ALWAYS Clear Current Scene First
461         R_ClearScene();
462
463         // Assign Standard Viewflags
464         // Draw the World (and sky)
465         R_SetView(VF_DRAWWORLD, 1);
466
467         // Set the console size vars
468         vid_conwidth = cvar("vid_conwidth");
469         vid_conheight = cvar("vid_conheight");
470         vid_pixelheight = cvar("vid_pixelheight");
471
472         R_SetView(VF_FOV, GetCurrentFov(fov));
473
474         // Camera for demo playback
475         if(camera_active)
476         {
477                 if(cvar("camera_enable"))
478                         CSQC_Demo_Camera();
479                 else
480                 {
481                         cvar_set("chase_active", ftos(chase_active_backup));
482                         cvar_set("cl_demo_mousegrab", "0");
483                         camera_active = FALSE;
484                 }
485         }
486 #ifdef CAMERATEST
487         else if(cvar("camera_enable"))
488 #else
489         else if(cvar("camera_enable") && isdemo())
490 #endif
491         {
492                 // Enable required Darkplaces cvars
493                 chase_active_backup = cvar("chase_active");
494                 cvar_set("chase_active", "2");
495                 cvar_set("cl_demo_mousegrab", "1");
496                 camera_active = TRUE;
497                 camera_mode = FALSE;
498         }
499
500         // Draw the Crosshair
501         float scoreboard_active;
502         scoreboard_active = Sbar_WouldDrawScoreboard();
503         R_SetView(VF_DRAWCROSSHAIR, 0); //Make sure engine crosshairs are always hidden
504
505         // Draw the Engine Status Bar (the default Quake HUD)
506         R_SetView(VF_DRAWENGINESBAR, 0);
507
508         // fetch this one only once per frame
509         sbar_showbinds = cvar("sbar_showbinds");
510         sbar_showbinds_limit = cvar("sbar_showbinds_limit");
511
512         // Update the mouse position
513         /*
514            mousepos_x = vid_conwidth;
515            mousepos_y = vid_conheight;
516            mousepos = mousepos*0.5 + getmousepos();
517          */
518
519         e = self;
520         for(self = world; (self = nextent(self)); )
521                 if(self.draw)
522                         self.draw();
523         self = e;
524
525         R_AddEntities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS);
526         R_RenderScene();
527
528         // now switch to 2D drawing mode by calling a 2D drawing function
529         // then polygon drawing will draw as 2D stuff, and NOT get queued until the
530         // next R_RenderScene call
531         drawstring('0 0 0', "", '1 1 0', '1 1 1', 0, 0);
532
533         // Draw the mouse cursor
534         // NOTE: drawpic must happen after R_RenderScene for some reason
535         //drawpic(getmousepos(), "gfx/cursor.tga", '11 14 0', '1 1 1', 1, 0);
536         //drawstring('50 50', ftos(game), '10 10 0', '1 1 1', 1, 0);
537         //self = edict_num(player_localnum);
538         //drawstring('0 0', vtos(pmove_org), '8 8 0', '1 1 1', 1, 0);
539         //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);
540         // as long as the ctf part isn't in, this is useless
541         if(menu_visible)
542                 menu_show();
543
544         /*if(gametype == GAME_CTF)
545           {
546           ctf_view();
547           } else */
548
549         // draw 2D entities
550         e = self;
551         for(self = world; (self = nextent(self)); )
552                 if(self.draw2d)
553                         self.draw2d();
554         self = e;
555
556         // draw radar
557         if(
558                         ons_showmap
559                         ||
560                         (
561                          !scoreboard_active
562                          &&
563                          cvar_string("cl_teamradar") != "0"
564                          &&
565                          (
566                           cvar("cl_teamradar") == 2
567                           ||
568                           teamplay
569                          )
570                         )
571           )
572                 teamradar_view();
573
574         // draw sbar
575         if(cvar("r_letterbox") == 0) {
576                 if (cvar("cl_showpressedkeys")) { // draw pressed keys when spectating and playing
577                         if(spectatee_status > 0 || cvar("cl_showpressedkeys") >= 2)
578                                 Sbar_DrawPressedKeys();
579                 }
580
581                 if (cvar("cl_showspeed"))
582                         Sbar_ShowSpeed();
583                 if (cvar("cl_showacceleration"))
584                         Sbar_ShowAcceleration();
585
586                 Sbar_DrawCenterPrint(); // draw centerprint messages even if viewsize >= 120
587         }
588
589         float hud;
590         hud = getstati(STAT_HUD);
591         if(hud == HUD_SPIDERBOT)
592         {
593                 CSQC_spider_HUD();
594         }
595         else if(hud == HUD_WAKIZASHI)
596         CSQC_WAKIZASHI_HUD();
597         else
598         {
599                 if(cvar("r_letterbox") == 0)
600                         if(cvar("viewsize") < 120)
601                                 CSQC_common_hud();
602
603                 // crosshair goes VERY LAST
604                 if(!scoreboard_active && !ons_showmap && !camera_active) {
605                         // TrueAim check
606                         float shottype;
607                         float bullets, ring_scale;
608                         // wcross_origin = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
609                         wcross_origin = project_3d_to_2d(view_origin + MAX_SHOT_DISTANCE * view_forward);
610                         wcross_origin_z = 0;
611                         if(cvar("crosshair_hittest"))
612                         {
613                                 vector wcross_oldorigin;
614                                 wcross_oldorigin = wcross_origin;
615                                 shottype = TrueAimCheck();
616                                 if(shottype == SHOTTYPE_HITWORLD)
617                                 {
618                                         v = wcross_origin - wcross_oldorigin;
619                                         v_x /= vid_conwidth;
620                                         v_y /= vid_conheight;
621                                         if(vlen(v) > 0.01)
622                                                 shottype = SHOTTYPE_HITOBSTRUCTION;
623                                 }
624                                 if(!cvar("crosshair_hittest_showimpact"))
625                                         wcross_origin = wcross_oldorigin;
626                         }
627                         else
628                                 shottype = SHOTTYPE_HITWORLD;
629
630                         string wcross_style;
631                         wcross_style = cvar_string("crosshair");
632
633                         if (wcross_style != "0") {
634                                 vector wcross_color, wcross_size;
635                                 string wcross_wep, wcross_name;
636                                 float wcross_alpha, wcross_scale, wcross_blur, wcross_resolution;
637
638                                 wcross_color_x = cvar("crosshair_color_red");
639                                 wcross_color_y = cvar("crosshair_color_green");
640                                 wcross_color_z = cvar("crosshair_color_blue");
641                                 wcross_alpha = cvar("crosshair_color_alpha");
642                                 wcross_resolution = cvar("crosshair_size");
643                                 if (cvar("crosshair_per_weapon")) {
644                                         e = get_weaponinfo(activeweapon);
645                                         if (e && e.netname != "")
646                                         {
647                                                 wcross_wep = e.netname;
648                                                 wcross_style = cvar_string(strcat("crosshair_", wcross_wep));
649                                                 if(wcross_style == "")
650                                                         wcross_style = e.netname;
651
652                                                 if(!cvar("crosshair_color_override"))
653                                                 {
654                                                         wcross_color_x = cvar(strcat("crosshair_", wcross_wep, "_color_red"));
655                                                         wcross_color_y = cvar(strcat("crosshair_", wcross_wep, "_color_green"));
656                                                         wcross_color_z = cvar(strcat("crosshair_", wcross_wep, "_color_blue"));
657                                                 }
658
659                                                 wcross_alpha *= cvar(strcat("crosshair_", wcross_wep, "_color_alpha"));
660                                                 wcross_resolution *= cvar(strcat("crosshair_", wcross_wep, "_size"));
661                                         }
662                                 }
663
664                                 wcross_name = strcat("gfx/crosshair", wcross_style);
665
666                                 if(cvar("crosshair_effect_scalefade"))
667                                 {
668                                         wcross_scale = wcross_resolution;
669                                         wcross_resolution = 1;
670                                 }
671                                 else
672                                 {
673                                         wcross_scale = 1;
674                                 }
675
676                                 if(shottype == SHOTTYPE_HITENEMY)
677                                         wcross_scale *= cvar("crosshair_hittest"); // is not queried if hittest is 0
678                                 if(shottype == SHOTTYPE_HITTEAM)
679                                         wcross_scale /= cvar("crosshair_hittest"); // is not queried if hittest is 0
680
681                                 f = cvar("crosshair_effect_speed");
682                                 if(f < 0)
683                                         f *= -2 * g_weaponswitchdelay;
684                                 if(wcross_scale != wcross_scale_goal_prev || wcross_alpha != wcross_alpha_goal_prev || wcross_color != wcross_color_goal_prev)
685                                 {
686                                         wcross_changedonetime = time + f;
687                                 }
688                                 if(wcross_name != wcross_name_goal_prev || wcross_resolution != wcross_resolution_goal_prev)
689                                 {
690                                         wcross_name_changestarttime = time;
691                                         wcross_name_changedonetime = time + f;
692                                         if(wcross_name_goal_prev_prev)
693                                                 strunzone(wcross_name_goal_prev_prev);
694                                         wcross_name_goal_prev_prev = wcross_name_goal_prev;
695                                         wcross_name_goal_prev = strzone(wcross_name);
696                                         wcross_name_alpha_goal_prev_prev = wcross_name_alpha_goal_prev;
697                                         wcross_resolution_goal_prev_prev = wcross_resolution_goal_prev;
698                                         wcross_resolution_goal_prev = wcross_resolution;
699                                 }
700
701                                 wcross_scale_goal_prev = wcross_scale;
702                                 wcross_alpha_goal_prev = wcross_alpha;
703                                 wcross_color_goal_prev = wcross_color;
704
705                                 if(shottype == SHOTTYPE_HITTEAM || (shottype == SHOTTYPE_HITOBSTRUCTION && cvar("crosshair_hittest_blur") && !cvar("chase_active")))
706                                 {
707                                         wcross_blur = 1;
708                                         wcross_alpha *= 0.75;
709                                 }
710                                 else
711                                         wcross_blur = 0;
712                                 // *_prev is at time-frametime
713                                 // * is at wcross_changedonetime+f
714                                 // what do we have at time?
715                                 if(time < wcross_changedonetime)
716                                 {
717                                         f = frametime / (wcross_changedonetime - time + frametime);
718                                         wcross_scale = f * wcross_scale + (1 - f) * wcross_scale_prev;
719                                         wcross_alpha = f * wcross_alpha + (1 - f) * wcross_alpha_prev;
720                                         wcross_color = f * wcross_color + (1 - f) * wcross_color_prev;
721                                 }
722
723                                 wcross_scale_prev = wcross_scale;
724                                 wcross_alpha_prev = wcross_alpha;
725                                 wcross_color_prev = wcross_color;
726
727                                 wcross_scale *= 1 - cvar("_menu_alpha");
728                                 wcross_alpha *= 1 - cvar("_menu_alpha");
729
730                                 wcross_size = drawgetimagesize(wcross_name) * wcross_scale;
731
732                                 // ring around crosshair representing bullets left in camping rifle clip
733                                 if (activeweapon == WEP_CAMPINGRIFLE)
734                                 {
735                                         ring_scale = cvar("crosshair_campingrifle_ring_size");
736                                         bullets = bound(0, getstati(STAT_BULLETS_LOADED), 8);
737                                 }
738                                 else
739                                         bullets = 0;
740
741 #define CROSSHAIR_DRAW_RING(i,j,sz,wcross_name,wcross_alpha) \
742                                 drawpic(wcross_origin - ('0.5 0 0' * (sz * wcross_size_x * ring_scale + i * wcross_blur) + '0 0.5 0' * (sz * wcross_size_y * ring_scale + j * wcross_blur)), strcat("gfx/hud/rifle_ring_", ftos(bullets)), sz * wcross_size * ring_scale, wcross_color, wcross_alpha, DRAWFLAG_NORMAL)
743
744 #define CROSSHAIR_DO_BLUR(M,sz,wcross_name,wcross_alpha) \
745                                 do \
746                                 { \
747                                         if(wcross_blur > 0) \
748                                         { \
749                                                 for(i = -2; i <= 2; ++i) \
750                                                         for(j = -2; j <= 2; ++j) \
751                                                                 M(i,j,sz,wcross_name,wcross_alpha*0.04); \
752                                         } \
753                                         else \
754                                         { \
755                                                 M(0,0,sz,wcross_name,wcross_alpha); \
756                                         } \
757                                 } \
758                                 while(0)
759
760 #define CROSSHAIR_DRAW_SINGLE(i,j,sz,wcross_name,wcross_alpha) \
761                                 drawpic(wcross_origin - ('0.5 0 0' * (sz * wcross_size_x + i * wcross_blur) + '0 0.5 0' * (sz * wcross_size_y + j * wcross_blur)), wcross_name, sz * wcross_size, wcross_color, wcross_alpha, DRAWFLAG_NORMAL)
762
763 #define CROSSHAIR_DRAW(sz,wcross_name,wcross_alpha) \
764                                 CROSSHAIR_DO_BLUR(CROSSHAIR_DRAW_SINGLE,sz,wcross_name,wcross_alpha)
765
766                                 if(bullets)
767                                 {
768                                         CROSSHAIR_DO_BLUR(CROSSHAIR_DRAW_RING, wcross_resolution, wcross_name, wcross_alpha);
769                                 }
770
771                                 if(time < wcross_name_changedonetime && wcross_name != wcross_name_goal_prev_prev && wcross_name_goal_prev_prev)
772                                 {
773                                         f = (wcross_name_changedonetime - time) / (wcross_name_changedonetime - wcross_name_changestarttime);
774                                         CROSSHAIR_DRAW(wcross_resolution_goal_prev_prev, wcross_name_goal_prev_prev, wcross_alpha * f * wcross_name_alpha_goal_prev_prev);
775                                         f = 1 - f;
776                                 }
777                                 else
778                                 {
779                                         f = 1;
780                                 }
781                                 CROSSHAIR_DRAW(wcross_resolution, wcross_name, wcross_alpha * f);
782                                 wcross_name_alpha_goal_prev = f;
783                         }
784                 }
785                 else
786                 {
787                         wcross_scale_prev = 0;
788                         wcross_alpha_prev = 0;
789                         wcross_scale_goal_prev = 0;
790                         wcross_alpha_goal_prev = 0;
791                         wcross_changedonetime = 0;
792                         if(wcross_name_goal_prev)
793                                 strunzone(wcross_name_goal_prev);
794                         wcross_name_goal_prev = string_null;
795                         if(wcross_name_goal_prev_prev)
796                                 strunzone(wcross_name_goal_prev_prev);
797                         wcross_name_goal_prev_prev = string_null;
798                         wcross_name_changestarttime = 0;
799                         wcross_name_changedonetime = 0;
800                         wcross_name_alpha_goal_prev = 0;
801                         wcross_name_alpha_goal_prev_prev = 0;
802                         wcross_resolution_goal_prev = 0;
803                         wcross_resolution_goal_prev_prev = 0;
804                 }
805         }
806
807         if(NextFrameCommand)
808         {
809                 localcmd("\n", NextFrameCommand, "\n");
810                 NextFrameCommand = string_null;
811         }
812
813         // we must do this check AFTER a frame was rendered, or it won't work
814         if(cs_project_is_b0rked == 0)
815         {
816                 string w0, h0;
817                 w0 = cvar_string("vid_conwidth");
818                 h0 = cvar_string("vid_conheight");
819                 //R_SetView(VF_VIEWPORT, '0 0 0', '640 480 0');
820                 //R_SetView(VF_FOV, '90 90 0');
821                 R_SetView(VF_ORIGIN, '0 0 0');
822                 R_SetView(VF_ANGLES, '0 0 0');
823                 R_SetView(VF_PERSPECTIVE, 1);
824                 makevectors('0 0 0');
825                 vector v1, v2;
826                 cvar_set("vid_conwidth", "800");
827                 cvar_set("vid_conheight", "600");
828                 v1 = cs_project(v_forward);
829                 cvar_set("vid_conwidth", "640");
830                 cvar_set("vid_conheight", "480");
831                 v2 = cs_project(v_forward);
832                 if(v1 == v2)
833                         cs_project_is_b0rked = 1;
834                 else
835                         cs_project_is_b0rked = -1;
836                 cvar_set("vid_conwidth", w0);
837                 cvar_set("vid_conheight", h0);
838         }
839
840         // be safe against triggerbots until everyone has the fixed engine
841         // this call is meant to overwrite the trace globals by something
842         // unsuspicious
843         traceline('0 0 0', '0 0 0', MOVE_WORLDONLY, world);
844 }
845
846 void Sbar_Draw();
847 void CSQC_spider_HUD()
848 {
849         float rockets,reload,heat,hp,shield,i;
850         vector p,pp;
851
852     p = drawgetimagesize(SPIDER_CROSS);
853     p_x *= cvar_or("cl_vehicle_spiderbot_cross_size", 1);
854     p_y *= cvar_or("cl_vehicle_spiderbot_cross_size", 1);
855     drawpic('0.5 0 0' * (vid_conwidth - p_x) + '0 0.5 0' * (vid_conheight - p_y), SPIDER_CROSS, p, '1 1 1', cvar_or("cl_vehicle_spiderbot_cross_alpha",0.6), DRAWFLAG_NORMAL);
856
857     hp      = bound(0,getstatf(STAT_VEHICLESTAT_HEALTH), 1);
858         shield  = bound(0,getstatf(STAT_VEHICLESTAT_SHIELD), 1);
859         heat    = min(getstatf(STAT_VEHICLESTAT_RELOAD1), 1);
860         rockets =     getstati(STAT_VEHICLESTAT_AMMO2);
861         reload  = min(getstatf(STAT_VEHICLESTAT_RELOAD2), 1);
862
863         // Draw health bar
864         p = '0.5 0 0' * (vid_conwidth - (rkt_size * 8));
865         p = p + '0 1 0' * vid_conheight - '0 32 0';
866         //pp = ('0 1 0' * hp) + ('1 0 0' * (1-hp));
867         drawfill(p, '256 0 0' * shield + '0 8 0' , '0.5 0.5 1', 0.75, DRAWFLAG_NORMAL);
868         p_y += 8;
869         drawfill(p, '256 0 0' * hp + '0 8 0' , '0 1 0', 0.75, DRAWFLAG_NORMAL);
870         p_x += 256 * hp;
871         drawfill(p, '256 0 0' * (1-hp) + '0 8 0' , '0 0 0', 0.75, DRAWFLAG_NORMAL);
872
873         // Draw minigun heat indicator
874         p = '0.5 0 0' * (vid_conwidth - 256);
875         p = p + '0 1 0' * vid_conheight - '0 34  0';
876         drawfill(p, '256 0 0' * (1-heat) + '0 2 0' ,'0 0 1', 0.5, DRAWFLAG_NORMAL);
877         p_x += 256 * (1-heat);
878         drawfill(p, '256 0 0' * heat  + '0 2 0' , '1 0 0', 0.5, DRAWFLAG_NORMAL);
879
880         // Draw rocket icons for loaded/empty tubes.
881         pp = '0.5 0 0' * (vid_conwidth - (rkt_size * 8));
882         pp += '0 1 0' * vid_conheight - '0 64 0';
883         for(i = 0; i < 8; ++i)
884         {
885                 p = pp + '1 0 0' * (rkt_size * i);
886                 if(rockets == 8)
887                 {
888                         if(floor(reload * 8) == i)
889                         {
890                                 drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '1 0 0' + '0 1 0' * ((reload*8)-i), 0.75 , DRAWFLAG_NORMAL);
891                         }
892                         else if(i < reload * 8)
893                                 drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '1 1 0', 0.75 , DRAWFLAG_NORMAL);
894                         else
895                                 drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '0.5 0.5 0.5', 0.75, DRAWFLAG_NORMAL);
896                 }
897                 else
898                 {
899                         if(i < rockets)
900                                 drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '0 0 0', 0.25, DRAWFLAG_NORMAL);
901                         else
902                                 drawpic(p, spider_rocket_icon, '1 1 0' * rkt_size, '0 1 0' * reload, 0.75, DRAWFLAG_NORMAL);
903                 }
904         }
905
906         if (sb_showscores)
907         {
908                 Sbar_DrawScoreboard();
909                 Sbar_DrawCenterPrint();
910         }
911
912 }
913
914 void CSQC_WAKIZASHI_HUD()
915 {
916         // 0--1 floats. 1 = 100%, 0.6 = 50%.
917         float health, shield, energy, rockets;
918
919     float i;
920         vector p, pp;
921
922     p = drawgetimagesize(SPIDER_CROSS);
923     p_x *= cvar_or("cl_vehicle_spiderbot_cross_size", 1);
924     p_y *= cvar_or("cl_vehicle_spiderbot_cross_size", 1);
925     drawpic('0.5 0 0' * (vid_conwidth - p_x) + '0 0.5 0' * (vid_conheight - p_y), SPIDER_CROSS, p, '1 1 1', cvar_or("cl_vehicle_spiderbot_cross_alpha",0.6), DRAWFLAG_NORMAL);
926
927 /*
928 const float STAT_VEHICLESTAT_HEALTH  = 60;
929 const float STAT_VEHICLESTAT_SHIELD  = 61;
930 const float STAT_VEHICLESTAT_ENERGY  = 62;
931 const float STAT_VEHICLESTAT_AMMO1   = 63;
932 const float STAT_VEHICLESTAT_RELAOD1 = 64;
933 const float STAT_VEHICLESTAT_AMMO2   = 65;
934 const float STAT_VEHICLESTAT_RELOAD2 = 66;
935 */
936     health  = min(getstatf(STAT_VEHICLESTAT_HEALTH),  1);
937         shield  = min(getstatf(STAT_VEHICLESTAT_SHIELD),  1);
938         energy  = min(getstatf(STAT_VEHICLESTAT_ENERGY),  1);
939         rockets = bound(0,getstatf(STAT_VEHICLESTAT_RELOAD1), 1);
940
941
942         p = '0.5 0 0' * (vid_conwidth - (rkt_size * 8));
943         p = p + '0 1 0' * vid_conheight - '0 32 0';
944
945         // Draw health bar
946         p_y += 8;
947         drawfill(p, '256 0 0' * health + '0 8 0' , '0 0.7 0', 0.75, DRAWFLAG_NORMAL);
948         p_x += 256 * health;
949         drawfill(p, '256 0 0' * (1 - health) + '0 8 0' , '0 0 0', 0.75, DRAWFLAG_NORMAL);
950
951         // Draw shiled bar
952         p_x -= 256 * health;
953         p_y += 4;
954         drawfill(p, '256 0 0' * shield + '0 4 0' , '0.25 0.25 1', 0.5, DRAWFLAG_NORMAL);
955
956         // Draw energy
957         //p_x -= 256 * health;
958         p_y -= 8;
959         drawfill(p, '256 0 0' * energy + '0 4 0' , '1 1 1', 0.75, DRAWFLAG_NORMAL);
960
961         // Draw rockets bar
962         p_y += 12;
963         drawfill(p, '256 0 0' * rockets + '0 4 0' , '1 0 0', 0.75, DRAWFLAG_NORMAL);
964
965
966         /*
967         // Draw energy bar
968         p = '0.5 0 0' * (vid_conwidth - 256);
969         p = p + '0 1 0' * vid_conheight - '0 34  0';
970         drawfill(p, '256 0 0' * (1 - energy) + '0 2 0' ,'0 0 1', 0.5, DRAWFLAG_NORMAL);
971         p_x += 256 * (1 - energy);
972         drawfill(p, '256 0 0' * energy  + '0 2 0' , '1 0 0', 0.5, DRAWFLAG_NORMAL);
973
974         // Draw rockets bar
975         p_y += 8;
976         drawfill(p, '256 0 0' * rockets + '0 8 0' , '1 0 0', 0.75, DRAWFLAG_NORMAL);
977         p_x += 256 * health;
978         drawfill(p, '256 0 0' * (1 - rockets) + '0 8 0' , '0 0 0', 0.75, DRAWFLAG_NORMAL);
979     */
980
981
982         if (sb_showscores)
983         {
984                 Sbar_DrawScoreboard();
985                 Sbar_DrawCenterPrint();
986         }
987
988 }
989
990
991 void CSQC_common_hud(void)
992 {
993         // Sbar_SortFrags(); done in Sbar_Draw
994         float hud;
995         hud = getstati(STAT_HUD);
996
997         //hud = 10;
998         switch(hud)
999         {
1000                 case HUD_NORMAL:
1001                         Sbar_Draw();
1002                         break;
1003
1004                 case HUD_SPIDERBOT:
1005                         CSQC_spider_HUD();
1006                         break;
1007
1008                 case HUD_WAKIZASHI:
1009                         CSQC_WAKIZASHI_HUD();
1010                         break;
1011         }
1012 }
1013
1014
1015 // following vectors must be global to allow seamless switching between camera modes
1016 vector camera_offset, current_camera_offset, mouse_angles, current_angles, current_origin, current_position;
1017 void CSQC_Demo_Camera()
1018 {
1019         float speed, attenuation, dimensions;
1020         vector tmp, delta;
1021
1022         if( cvar("camera_reset") || !camera_mode )
1023         {
1024                 camera_offset = '0 0 0';
1025                 current_angles = '0 0 0';
1026                 camera_direction = '0 0 0';
1027                 camera_offset_z += 30;
1028                 camera_offset_x += 30 * -cos(current_angles_y * DEG2RAD);
1029                 camera_offset_y += 30 * -sin(current_angles_y * DEG2RAD);
1030                 current_origin = view_origin;
1031                 current_camera_offset  = camera_offset;
1032                 cvar_set("camera_reset", "0");
1033                 camera_mode = CAMERA_CHASE;
1034         }
1035
1036         // Camera angles
1037         if( camera_roll )
1038                 mouse_angles_z += camera_roll * cvar("camera_speed_roll");
1039
1040         if(cvar("camera_look_player"))
1041         {
1042                 local vector dir;
1043                 local float n;
1044
1045                 dir = normalize(view_origin - current_position);
1046                 n = mouse_angles_z;
1047                 mouse_angles = vectoangles(dir);
1048                 mouse_angles_x = mouse_angles_x * -1;
1049                 mouse_angles_z = n;
1050         }
1051         else
1052         {
1053                 tmp = getmousepos() * 0.1;
1054                 if(vlen(tmp)>cvar("camera_mouse_treshold"))
1055                 {
1056                         mouse_angles_x += tmp_y * cos(mouse_angles_z * DEG2RAD) + (tmp_x * sin(mouse_angles_z * DEG2RAD));
1057                         mouse_angles_y -= tmp_x * cos(mouse_angles_z * DEG2RAD) + (tmp_y * -sin(mouse_angles_z * DEG2RAD));
1058                 }
1059         }
1060
1061         while (mouse_angles_x < -180) mouse_angles_x = mouse_angles_x + 360;
1062         while (mouse_angles_x > 180) mouse_angles_x = mouse_angles_x - 360;
1063         while (mouse_angles_y < -180) mouse_angles_y = mouse_angles_y + 360;
1064         while (mouse_angles_y > 180) mouse_angles_y = mouse_angles_y - 360;
1065
1066         // Fix difference when angles don't have the same sign
1067         delta = '0 0 0';
1068         if(mouse_angles_y < -60 && current_angles_y > 60)
1069                 delta = '0 360 0';
1070         if(mouse_angles_y > 60 && current_angles_y < -60)
1071                 delta = '0 -360 0';
1072
1073         if(cvar("camera_look_player"))
1074                 attenuation = cvar("camera_look_attenuation");
1075         else
1076                 attenuation = cvar("camera_speed_attenuation");
1077
1078         attenuation = 1 / max(1, attenuation);
1079         current_angles += (mouse_angles - current_angles + delta) * attenuation;
1080
1081         while (current_angles_x < -180) current_angles_x = current_angles_x + 360;
1082         while (current_angles_x > 180) current_angles_x = current_angles_x - 360;
1083         while (current_angles_y < -180) current_angles_y = current_angles_y + 360;
1084         while (current_angles_y > 180) current_angles_y = current_angles_y - 360;
1085
1086         // Camera position
1087         tmp = '0 0 0';
1088         dimensions = 0;
1089
1090         if( camera_direction_x )
1091         {
1092                 tmp_x = camera_direction_x * cos(current_angles_y * DEG2RAD);
1093                 tmp_y = camera_direction_x * sin(current_angles_y * DEG2RAD);
1094                 if( cvar("camera_forward_follows") && !cvar("camera_look_player") )
1095                         tmp_z = camera_direction_x * -sin(current_angles_x * DEG2RAD);
1096                 ++dimensions;
1097         }
1098
1099         if( camera_direction_y )
1100         {
1101                 tmp_x += camera_direction_y * -sin(current_angles_y * DEG2RAD);
1102                 tmp_y += camera_direction_y * cos(current_angles_y * DEG2RAD) * cos(current_angles_z * DEG2RAD);
1103                 tmp_z += camera_direction_y * sin(current_angles_z * DEG2RAD);
1104                 ++dimensions;
1105         }
1106
1107         if( camera_direction_z )
1108         {
1109                 tmp_z += camera_direction_z * cos(current_angles_z * DEG2RAD);
1110                 ++dimensions;
1111         }
1112
1113         if(cvar("camera_free"))
1114                 speed = cvar("camera_speed_free");
1115         else
1116                 speed = cvar("camera_speed_chase");
1117
1118         if(dimensions)
1119         {
1120                 speed = speed * sqrt(1 / dimensions);
1121                 camera_offset += tmp * speed;
1122         }
1123
1124         current_camera_offset += (camera_offset - current_camera_offset) * attenuation;
1125
1126         // Camera modes
1127         if( cvar("camera_free") )
1128         {
1129                 if ( camera_mode == CAMERA_CHASE )
1130                 {
1131                         current_camera_offset = current_origin + current_camera_offset;
1132                         camera_offset = current_origin + camera_offset;
1133                 }
1134
1135                 camera_mode = CAMERA_FREE;
1136                 current_position = current_camera_offset;
1137         }
1138         else
1139         {
1140                 if ( camera_mode == CAMERA_FREE )
1141                 {
1142                         current_origin = view_origin;
1143                         camera_offset = camera_offset - current_origin;
1144                         current_camera_offset = current_camera_offset - current_origin;
1145                 }
1146
1147                 camera_mode = CAMERA_CHASE;
1148
1149                 if(cvar("camera_chase_smoothly"))
1150                         current_origin += (view_origin - current_origin) * attenuation;
1151                 else
1152                         current_origin = view_origin;
1153
1154                 current_position = current_origin + current_camera_offset;
1155         }
1156
1157         R_SetView(VF_ANGLES, current_angles);
1158         R_SetView(VF_ORIGIN, current_position);
1159 }