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