]> icculus.org git repositories - divverent/nexuiz.git/blob - data/qcsrc/client/waypointsprites.qc
support waypointsprite fading near the crosshair and screen edges
[divverent/nexuiz.git] / data / qcsrc / client / waypointsprites.qc
1 float waypointsprite_initialized;
2 float waypointsprite_fadedistance;
3 float waypointsprite_normdistance;
4 float waypointsprite_minscale;
5 float waypointsprite_minalpha;
6 float waypointsprite_distancealphaexponent;
7 float waypointsprite_timealphaexponent;
8 float waypointsprite_scale;
9
10 .float rule;
11 .string netname; // primary picture
12 .string netname2; // secondary picture
13 .string netname3; // tertiary picture
14 .float team; // team that gets netname2
15 .float lifetime;
16 .float fadetime;
17 .float maxdistance;
18 .float hideflags;
19 .float spawntime;
20 .float health;
21 .float build_started;
22 .float build_starthealth;
23 .float build_finished;
24
25 vector SPRITE_SIZE = '288 36 0';
26 vector SPRITE_HOTSPOT = '144 36 0';
27 float SPRITE_HEALTHBAR_WIDTH = 96;
28 float SPRITE_HEALTHBAR_HEIGHT = 6;
29 float SPRITE_HEALTHBAR_MARGIN = 4;
30 float SPRITE_HEALTHBAR_BORDER = 1;
31 float SPRITE_HEALTHBAR_BORDERALPHA = 1;
32 float SPRITE_HEALTHBAR_HEALTHALPHA = 0.5;
33
34 void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, vector rgb, float a, float f)
35 {
36         vector v1, v2, v3, v4;
37
38         hotspot = -1 * hotspot;
39
40         // hotspot-relative coordinates of the corners
41         v1 = hotspot;
42         v2 = hotspot + '1 0 0' * sz_x;
43         v3 = hotspot + '1 0 0' * sz_x + '0 1 0' * sz_y;
44         v4 = hotspot                  + '0 1 0' * sz_y;
45
46         // rotate them, and make them absolute
47         rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
48         v1 = rotate(v1, rot) + org;
49         v2 = rotate(v2, rot) + org;
50         v3 = rotate(v3, rot) + org;
51         v4 = rotate(v4, rot) + org;
52
53         // draw them
54         R_BeginPolygon(pic, f);
55         R_PolygonVertex(v1, '0 0 0', rgb, a);
56         R_PolygonVertex(v2, '1 0 0', rgb, a);
57         R_PolygonVertex(v3, '1 1 0', rgb, a);
58         R_PolygonVertex(v4, '0 1 0', rgb, a);
59         R_EndPolygon();
60 }
61
62 void drawquad(vector o, vector ri, vector up, string pic, vector rgb, float a, float f)
63 {
64         R_BeginPolygon(pic, f);
65         R_PolygonVertex(o, '0 0 0', rgb, a);
66         R_PolygonVertex(o + ri, '1 0 0', rgb, a);
67         R_PolygonVertex(o + up + ri, '1 1 0', rgb, a);
68         R_PolygonVertex(o + up, '0 1 0', rgb, a);
69         R_EndPolygon();
70 }
71
72 void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, float width, float height, float margin, float border, float align, vector rgb, float a, vector hrgb, float ha, float f)
73 {
74         vector o, ri, up;
75         float owidth; // outer width
76
77         hotspot = -1 * hotspot;
78
79         // hotspot-relative coordinates of the healthbar corners
80         o = hotspot;
81         ri = '1 0 0';
82         up = '0 1 0';
83         
84         rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed
85         o = rotate(o, rot) + org;
86         ri = rotate(ri, rot);
87         up = rotate(up, rot);
88
89         owidth = width + 2 * border;
90         o = o - up * (margin + border + height) + ri * (sz_x - owidth) * 0.5;
91
92         drawquad(o - up * border,                               ri * owidth,    up * border, "", rgb,  a,  f);
93         drawquad(o + up * height,                               ri * owidth,    up * border, "", rgb,  a,  f);
94         drawquad(o,                                             ri * border,    up * height, "", rgb,  a,  f);
95         drawquad(o + ri * (owidth - border),                    ri * border,    up * height, "", rgb,  a,  f);
96         drawquad(o + ri * (border + align * ((1 - h) * width)), ri * width * h, up * height, "", hrgb, ha, f);
97 }
98
99 void Draw_WaypointSprite()
100 {
101         string spriteimage;
102         float t;
103
104         if(self.lifetime)
105                 self.alpha = pow(bound(0, (self.fadetime - time) / self.lifetime, 1), waypointsprite_timealphaexponent);
106         else
107                 self.alpha = 1;
108
109         if(self.hideflags & 2)
110                 return; // radar only
111
112         if(cvar("cl_hidewaypoints") >= 2)
113                 return;
114
115         if(self.hideflags & 1)
116                 if(cvar("cl_hidewaypoints"))
117                         return; // fixed waypoint
118
119         InterpolateOrigin_Do();
120
121         t = GetPlayerColor(player_localentnum - 1) + 1;
122
123         spriteimage = "";
124
125         // choose the sprite
126         switch(self.rule)
127         {
128                 case SPRITERULE_DEFAULT:
129                         if(self.team)
130                         {
131                                 if(self.team == t)
132                                         spriteimage = self.netname;
133                                 else
134                                         spriteimage = "";
135                         }
136                         else
137                                 spriteimage = self.netname;
138                         break;
139                 case SPRITERULE_TEAMPLAY:
140                         if(t == COLOR_SPECTATOR + 1)
141                                 spriteimage = self.netname3;
142                         else if(self.team == t)
143                                 spriteimage = self.netname2;
144                         else
145                                 spriteimage = self.netname;
146                         break;
147                 default:
148                         error("Invalid waypointsprite rule!");
149                         break;
150         }
151
152         if(spriteimage == "")
153                 return;
154         
155         float dist;
156         dist = vlen(self.origin - view_origin);
157         
158         float a;
159         a = self.alpha * sbar_alpha_fg;
160
161         if(self.maxdistance > waypointsprite_normdistance)
162                 a *= pow(bound(0, (self.maxdistance - dist) / (self.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent);
163         else if(self.maxdistance > 0)
164                 a *= pow(bound(0, (waypointsprite_fadedistance - dist) / (waypointsprite_fadedistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent) * (1 - waypointsprite_minalpha) + waypointsprite_minalpha;
165
166         if(a <= 0)
167                 return;
168         
169         // draw the sprite image
170         vector o;
171         float rot;
172         o = project_3d_to_2d(self.origin);
173         rot = 0;
174
175         if(o_z < 0 || o_x < 0 || o_y < 0 || o_x > vid_conwidth || o_y > vid_conheight)
176         {
177                 // scale it to be just in view
178                 vector d;
179                 float f1, f2;
180
181                 // get the waypoint angle vector
182                 /*
183                 d_x = view_right * (self.origin - view_origin) * vid_conwidth / vid_width;
184                 d_y = -view_up * (self.origin - view_origin) * vid_conheight / (vid_height * vid_pixelheight);
185                 d_z = 0;
186                 */
187                 
188                 d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight;
189
190                 /*
191                 if(cvar("v_flipped"))
192                         d_x = -d_x;
193                 */
194
195                 f1 = d_x / vid_conwidth;
196                 f2 = d_y / vid_conheight;
197
198                 if(max(f1, -f1) > max(f2, -f2))
199                 {
200                         if(d_z * f1 > 0)
201                         {
202                                 // RIGHT edge
203                                 d = d * (0.5 / f1);
204                                 rot = 3;
205                         }
206                         else
207                         {
208                                 // LEFT edge
209                                 d = d * (-0.5 / f1);
210                                 rot = 1;
211                         }
212                 }
213                 else
214                 {
215                         if(d_z * f2 > 0)
216                         {
217                                 // BOTTOM edge
218                                 d = d * (0.5 / f2);
219                                 rot = 0;
220                         }
221                         else
222                         {
223                                 // TOP edge
224                                 d = d * (-0.5 / f2);
225                                 rot = 2;
226                         }
227                 }
228
229                 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
230         }
231         o_z = 0;
232
233         float vidscale;
234         vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
235
236         t = stof(db_get(tempdb, strcat("/spriteframes/", spriteimage)));
237         if(t == 0)
238                 spriteimage = strcat("models/sprites/", spriteimage);
239         else
240                 spriteimage = strcat("models/sprites/", spriteimage, "_frame", ftos(mod(floor((max(0, time - self.spawntime)) * 2), t)));
241
242         float edgefadealpha = cvar("g_waypointsprite_edgefadealpha");
243         float edgefadescale = cvar("g_waypointsprite_edgefadescale");
244         float edgefadedistance = cvar("g_waypointsprite_edgefadedistance");
245         float edgedistance_min = min4(o_y, o_x,vid_conwidth - o_x, vid_conheight - o_y);
246
247         float crosshairfadealpha = cvar("g_waypointsprite_crosshairfadealpha");
248         float crosshairfadescale = cvar("g_waypointsprite_crosshairfadescale");
249         float crosshairfadedistance = cvar("g_waypointsprite_crosshairfadedistance");
250         float crosshairdistance; // current distance from waypoint to crosshair
251         crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) );
252
253         if (edgedistance_min < edgefadedistance) {
254                 a = a * bound(edgefadealpha, edgedistance_min/edgefadedistance, 1);
255                 vidscale = vidscale * bound(edgefadescale, edgedistance_min/edgefadedistance, 1);
256         } else if (crosshairdistance < crosshairfadedistance) {
257                 a = a * bound(crosshairfadealpha, crosshairdistance/crosshairfadedistance, 1);
258                 vidscale = vidscale * bound(crosshairfadescale, crosshairdistance/crosshairfadedistance, 1);
259         }
260         drawrotpic(o, rot * 90 * DEG2RAD, spriteimage, SPRITE_SIZE * waypointsprite_scale * vidscale, SPRITE_HOTSPOT * waypointsprite_scale * vidscale, '1 1 1', a, DRAWFLAG_MIPMAP);
261
262         if(self.build_finished)
263         {
264                 if(time < self.build_finished + 0.25)
265                 {
266                         if(time < self.build_started)
267                                 self.health = self.build_starthealth;
268                         else if(time < self.build_finished)
269                                 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
270                         else
271                                 self.health = 1;
272                 }
273                 else
274                         self.health = -1;
275         }
276
277         if(self.health >= 0)
278         {
279                 float align;
280                 if(self.build_finished)
281                         align = 0.5;
282                 else
283                         align = 0;
284                 drawhealthbar(o, rot * 90 * DEG2RAD, self.health, SPRITE_SIZE * waypointsprite_scale * vidscale, SPRITE_HOTSPOT * waypointsprite_scale * vidscale, SPRITE_HEALTHBAR_WIDTH, SPRITE_HEALTHBAR_HEIGHT, SPRITE_HEALTHBAR_MARGIN, SPRITE_HEALTHBAR_BORDER, align, self.teamradar_color, a * SPRITE_HEALTHBAR_BORDERALPHA, self.teamradar_color, a * SPRITE_HEALTHBAR_HEALTHALPHA, DRAWFLAG_NORMAL);
285         }
286 }
287
288 void Ent_RemoveWaypointSprite()
289 {
290         if(self.netname)
291                 strunzone(self.netname);
292         if(self.netname2)
293                 strunzone(self.netname2);
294         if(self.netname3)
295                 strunzone(self.netname3);
296 }
297
298 void Ent_WaypointSprite()
299 {
300         float sendflags, f, t;
301         sendflags = ReadByte();
302
303         if(!self.spawntime)
304                 self.spawntime = time;
305
306         self.draw2d = Draw_WaypointSprite;
307
308         InterpolateOrigin_Undo();
309
310         if(sendflags & 0x80)
311         {
312                 t = ReadByte();
313                 if(t < 192)
314                 {
315                         self.health = t / 191.0;
316                         self.build_finished = 0;
317                 }
318                 else
319                 {
320                         t = (t - 192) * 256 + ReadByte();
321                         self.build_started = servertime;
322                         if(self.build_finished)
323                                 self.build_starthealth = bound(0, self.health, 1);
324                         else
325                                 self.build_starthealth = 0;
326                         self.build_finished = servertime + t / 32;
327                         //print("build: ", ftos(self.build_finished - self.build_started), "\n");
328                 }
329         }
330         else
331         {
332                 self.health = -1;
333                 self.build_finished = 0;
334         }
335
336         if(sendflags & 64)
337         {
338                 // unfortunately, this needs to be exact (for the 3D display)
339                 self.origin_x = ReadCoord();
340                 self.origin_y = ReadCoord();
341                 self.origin_z = ReadCoord();
342         }
343
344         if(sendflags & 1)
345         {
346                 self.team = ReadByte();
347                 self.rule = ReadByte();
348         }
349
350         if(sendflags & 2)
351         {
352                 if(self.netname)
353                         strunzone(self.netname);
354                 self.netname = strzone(ReadString());
355         }
356
357         if(sendflags & 4)
358         {
359                 if(self.netname2)
360                         strunzone(self.netname2);
361                 self.netname2 = strzone(ReadString());
362         }
363
364         if(sendflags & 8)
365         {
366                 if(self.netname3)
367                         strunzone(self.netname3);
368                 self.netname3 = strzone(ReadString());
369         }
370
371         if(sendflags & 16)
372         {
373                 self.lifetime = ReadCoord();
374                 self.fadetime = ReadCoord();
375                 self.maxdistance = ReadShort();
376                 self.hideflags = ReadByte();
377         }
378
379         if(sendflags & 32)
380         {
381                 f = ReadByte();
382                 self.teamradar_icon = (f & 0x7F);
383                 if(f & 0x80)
384                 {
385                         self.(teamradar_times[self.teamradar_time_index]) = time;
386                         self.teamradar_time_index = mod(self.teamradar_time_index + 1, MAX_TEAMRADAR_TIMES);
387                 }
388                 self.teamradar_color_x = ReadByte() / 255.0;
389                 self.teamradar_color_y = ReadByte() / 255.0;
390                 self.teamradar_color_z = ReadByte() / 255.0;
391         }
392
393         InterpolateOrigin_Note();
394
395         self.entremove = Ent_RemoveWaypointSprite;
396 }
397
398 void WaypointSprite_Load()
399 {
400         waypointsprite_fadedistance = vlen(world.maxs - world.mins);
401         waypointsprite_normdistance = cvar("g_waypointsprite_normdistance");
402         waypointsprite_minscale = cvar("g_waypointsprite_minscale");
403         waypointsprite_minalpha = cvar("g_waypointsprite_minalpha");
404         waypointsprite_distancealphaexponent = cvar("g_waypointsprite_distancealphaexponent");
405         waypointsprite_timealphaexponent = cvar("g_waypointsprite_timealphaexponent");
406         waypointsprite_scale = cvar("g_waypointsprite_scale") * (1 - cvar("_menu_alpha"));
407         if(!waypointsprite_scale)
408                 waypointsprite_scale = 1.0 * (1 - cvar("_menu_alpha"));
409
410         if(!waypointsprite_initialized)
411         {
412                 float dh, n, i, o, f;
413                 string s, sname, sframes;
414                 dh = search_begin("models/sprites/*_frame*.tga", FALSE, FALSE);
415                 n = search_getsize(dh);
416                 for(i = 0; i < n; ++i)
417                 {
418                         s = search_getfilename(dh, i);
419                         if(substring(s, 0, 15) != "models/sprites/")
420                                 continue;
421                         if(substring(s, strlen(s) - 4, 4) != ".tga")
422                                 continue;
423                         s = substring(s, 15, strlen(s) - 19);
424
425                         o = strstrofs(s, "_frame", 0);
426                         sname = strcat("/spriteframes/", substring(s, 0, o));
427                         sframes = substring(s, o + 6, strlen(s) - o - 6);
428                         f = stof(sframes) + 1;
429                         db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
430                 }
431                 search_end(dh);
432         }
433         waypointsprite_initialized = 1;
434 }