give electro secondary a trail like in Nexuiz 1.0 :P
[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                 if(cvar("v_flipped"))
191                         d_x = -d_x;
192
193                 f1 = d_x / vid_conwidth;
194                 f2 = d_y / vid_conheight;
195
196                 if(max(f1, -f1) > max(f2, -f2))
197                 {
198                         if(d_z * f1 > 0)
199                         {
200                                 // RIGHT edge
201                                 d = d * (0.5 / f1);
202                                 rot = 3;
203                         }
204                         else
205                         {
206                                 // LEFT edge
207                                 d = d * (-0.5 / f1);
208                                 rot = 1;
209                         }
210                 }
211                 else
212                 {
213                         if(d_z * f2 > 0)
214                         {
215                                 // BOTTOM edge
216                                 d = d * (0.5 / f2);
217                                 rot = 0;
218                         }
219                         else
220                         {
221                                 // TOP edge
222                                 d = d * (-0.5 / f2);
223                                 rot = 2;
224                         }
225                 }
226
227                 o = d + '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight;
228         }
229         o_z = 0;
230
231         float vidscale;
232         vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height);
233
234         t = stof(db_get(tempdb, strcat("/spriteframes/", spriteimage)));
235         if(t == 0)
236                 spriteimage = strcat("models/sprites/", spriteimage);
237         else
238                 spriteimage = strcat("models/sprites/", spriteimage, "_frame", ftos(mod(floor((max(0, time - self.spawntime)) * 2), t)));
239
240         drawrotpic(o, rot * 90 * DEG2RAD, spriteimage, SPRITE_SIZE * waypointsprite_scale * vidscale, SPRITE_HOTSPOT * waypointsprite_scale * vidscale, '1 1 1', a, DRAWFLAG_MIPMAP);
241
242         if(self.build_finished)
243         {
244                 if(time < self.build_finished + 0.25)
245                 {
246                         if(time < self.build_started)
247                                 self.health = self.build_starthealth;
248                         else if(time < self.build_finished)
249                                 self.health = (time - self.build_started) / (self.build_finished - self.build_started) * (1 - self.build_starthealth) + self.build_starthealth;
250                         else
251                                 self.health = 1;
252                 }
253                 else
254                         self.health = -1;
255         }
256
257         if(self.health >= 0)
258         {
259                 float align;
260                 if(self.build_finished)
261                         align = 0.5;
262                 else
263                         align = 0;
264                 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);
265         }
266 }
267
268 void Ent_RemoveWaypointSprite()
269 {
270         if(self.netname)
271                 strunzone(self.netname);
272         if(self.netname2)
273                 strunzone(self.netname2);
274         if(self.netname3)
275                 strunzone(self.netname3);
276 }
277
278 void Ent_WaypointSprite()
279 {
280         float sendflags, f, t;
281         sendflags = ReadByte();
282
283         if(!self.spawntime)
284                 self.spawntime = time;
285
286         self.draw2d = Draw_WaypointSprite;
287
288         InterpolateOrigin_Undo();
289
290         if(sendflags & 0x80)
291         {
292                 t = ReadByte();
293                 if(t < 192)
294                 {
295                         self.health = t / 191.0;
296                         self.build_finished = 0;
297                 }
298                 else
299                 {
300                         t = (t - 192) * 256 + ReadByte();
301                         self.build_started = servertime;
302                         if(self.build_finished)
303                                 self.build_starthealth = bound(0, self.health, 1);
304                         else
305                                 self.build_starthealth = 0;
306                         self.build_finished = servertime + t / 32;
307                         //print("build: ", ftos(self.build_finished - self.build_started), "\n");
308                 }
309         }
310         else
311         {
312                 self.health = -1;
313                 self.build_finished = 0;
314         }
315
316         if(sendflags & 64)
317         {
318                 // unfortunately, this needs to be exact (for the 3D display)
319                 self.origin_x = ReadCoord();
320                 self.origin_y = ReadCoord();
321                 self.origin_z = ReadCoord();
322         }
323
324         if(sendflags & 1)
325         {
326                 self.team = ReadByte();
327                 self.rule = ReadByte();
328         }
329
330         if(sendflags & 2)
331         {
332                 if(self.netname)
333                         strunzone(self.netname);
334                 self.netname = strzone(ReadString());
335         }
336
337         if(sendflags & 4)
338         {
339                 if(self.netname2)
340                         strunzone(self.netname2);
341                 self.netname2 = strzone(ReadString());
342         }
343
344         if(sendflags & 8)
345         {
346                 if(self.netname3)
347                         strunzone(self.netname3);
348                 self.netname3 = strzone(ReadString());
349         }
350
351         if(sendflags & 16)
352         {
353                 self.lifetime = ReadCoord();
354                 self.fadetime = ReadCoord();
355                 self.maxdistance = ReadShort();
356                 self.hideflags = ReadByte();
357         }
358
359         if(sendflags & 32)
360         {
361                 f = ReadByte();
362                 self.teamradar_icon = (f & 0x7F);
363                 if(f & 0x80)
364                 {
365                         self.(teamradar_times[self.teamradar_time_index]) = time;
366                         self.teamradar_time_index = mod(self.teamradar_time_index + 1, MAX_TEAMRADAR_TIMES);
367                 }
368                 self.teamradar_color_x = ReadByte() / 255.0;
369                 self.teamradar_color_y = ReadByte() / 255.0;
370                 self.teamradar_color_z = ReadByte() / 255.0;
371         }
372
373         InterpolateOrigin_Note();
374
375         self.entremove = Ent_RemoveWaypointSprite;
376 }
377
378 void WaypointSprite_Load()
379 {
380         waypointsprite_fadedistance = vlen(world.maxs - world.mins);
381         waypointsprite_normdistance = cvar("g_waypointsprite_normdistance");
382         waypointsprite_minscale = cvar("g_waypointsprite_minscale");
383         waypointsprite_minalpha = cvar("g_waypointsprite_minalpha");
384         waypointsprite_distancealphaexponent = cvar("g_waypointsprite_distancealphaexponent");
385         waypointsprite_timealphaexponent = cvar("g_waypointsprite_timealphaexponent");
386         waypointsprite_scale = cvar("g_waypointsprite_scale") * (1 - cvar("_menu_alpha"));
387         if(!waypointsprite_scale)
388                 waypointsprite_scale = 1.0 * (1 - cvar("_menu_alpha"));
389
390         if(!waypointsprite_initialized)
391         {
392                 float dh, n, i, o, f;
393                 string s, sname, sframes;
394                 dh = search_begin("models/sprites/*_frame*.tga", FALSE, FALSE);
395                 n = search_getsize(dh);
396                 for(i = 0; i < n; ++i)
397                 {
398                         s = search_getfilename(dh, i);
399                         if(substring(s, 0, 15) != "models/sprites/")
400                                 continue;
401                         if(substring(s, strlen(s) - 4, 4) != ".tga")
402                                 continue;
403                         s = substring(s, 15, strlen(s) - 19);
404
405                         o = strstrofs(s, "_frame", 0);
406                         sname = strcat("/spriteframes/", substring(s, 0, o));
407                         sframes = substring(s, o + 6, strlen(s) - o - 6);
408                         f = stof(sframes) + 1;
409                         db_put(tempdb, sname, ftos(max(f, stof(db_get(tempdb, sname)))));
410                 }
411                 search_end(dh);
412         }
413         waypointsprite_initialized = 1;
414 }